ABC 015 D 高橋くんの苦悩

自分がやりたいことは、自分が実行しない限り一生達成されることはない。えぬわいです。

qiita.com

続きです。

atcoder.jp

問題のURLです。

DPな日々。ちょっと慣れてきました。

ナップザック問題に、個数の制限がかかったような問題です。

限界重さと、選ぶことができる最大の個数が決まっており、その中から最大の価値を捻出するという概要。

W = int(input())
N, K = map(int, input().split())
a, b = ([], [])

for i in range(N):
    aa, bb = map(int, input().split())
    a.append(aa); b.append(bb)

"""
def rec(i, j, k):
    # i 番目以降から j 個選んだ時の選んだ時の最大の価値を返す
    if i >= N:
        return 0
    if j >= K:
        return 0
    ans = 0
    # 入れる
    if k >= a[i]:
        ans = rec(i+1, j+1, k-a[i])+b[i]
    ans = max(rec(i+1, j, k), ans)
    return ans

print(rec(0, 0, W))
"""

dp = [[[0 for z in range(W+1)] for y in range(K+1)] for x in range(N+1)]

# N が初期値が入っている段になっているので、N-1から。
# また、0 にも入れたいので、0 を含む
# O(NKW) になってTLE
for i in range(N-1, -1, -1):
    for j in range(K-1, -1, -1):
        # W に入れたいので
        for k in range(W+1):
            if k >= a[i]:
                dp[i][j][k] = max(dp[i+1][j+1][k-a[i]]+b[i], dp[i+1][j][k])
            else:
                dp[i][j][k] = dp[i+1][j][k]

print(dp[0][0][W])

前半部分に、再帰関数が書いてあります。後半で、DP化。

pythonで書くと、TLEで死ねます。これは、TLEです。

cpp で書き直します。

#include <cstdio>
#include <algorithm>
using namespace std;

int dp[50+1][50+1][10000+1], a[50], b[50];

int W, N, K;

int main() {
    scanf("%d", &W);
    scanf("%d %d", &N, &K);
    for (int i = 0; i < N; i++) {
        scanf("%d %d", &a[i], &b[i]);
    }

    for (int i = N-1; i >= 0; i--) {
        for (int j = K-1; j >= 0; j--) {
            for (int k = 0; k <= W; k++) {
                if (k >= a[i]) {
                    dp[i][j][k] = max(dp[i+1][j+1][k-a[i]]+b[i], dp[i+1][j][k]);
                } else {
                    dp[i][j][k] = dp[i+1][j][k];
                }
            }
        }
    }
    printf("%d\n", dp[0][0][W]);
    return 0;
}

python でTLEったときは、DPがおかしいのかなと思ったけど、c++で試したら通ったんで、まぁいいのかなという感じです。

パッと見で、問題がナップザックっぽいなって思えたのは、成長かも。なにくそにゃー、まだ成長するかもらー。

この調子でやり続けたいと思います。