結果

問題 No.2869 yuusaan's Knapsacks
ユーザー InTheBloomInTheBloom
提出日時 2024-08-31 09:27:54
言語 D
(dmd 2.106.1)
結果
AC  
実行時間 1,171 ms / 4,500 ms
コード長 2,467 bytes
コンパイル時間 5,508 ms
コンパイル使用メモリ 176,836 KB
実行使用メモリ 18,160 KB
最終ジャッジ日時 2024-08-31 09:28:11
合計ジャッジ時間 16,267 ms
ジャッジサーバーID
(参考情報)
judge2 / judge1
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1 ms
6,816 KB
testcase_01 AC 1 ms
6,816 KB
testcase_02 AC 2 ms
6,940 KB
testcase_03 AC 40 ms
8,348 KB
testcase_04 AC 41 ms
9,608 KB
testcase_05 AC 1 ms
6,940 KB
testcase_06 AC 777 ms
18,160 KB
testcase_07 AC 132 ms
8,472 KB
testcase_08 AC 9 ms
6,940 KB
testcase_09 AC 9 ms
6,944 KB
testcase_10 AC 274 ms
12,104 KB
testcase_11 AC 27 ms
6,944 KB
testcase_12 AC 678 ms
14,676 KB
testcase_13 AC 22 ms
8,744 KB
testcase_14 AC 282 ms
9,728 KB
testcase_15 AC 74 ms
9,564 KB
testcase_16 AC 158 ms
11,164 KB
testcase_17 AC 1,171 ms
17,380 KB
testcase_18 AC 736 ms
13,880 KB
testcase_19 AC 470 ms
11,604 KB
testcase_20 AC 9 ms
6,940 KB
testcase_21 AC 495 ms
12,168 KB
testcase_22 AC 680 ms
12,108 KB
testcase_23 AC 23 ms
7,540 KB
testcase_24 AC 924 ms
14,448 KB
testcase_25 AC 16 ms
6,944 KB
testcase_26 AC 1,029 ms
12,688 KB
testcase_27 AC 2 ms
6,940 KB
testcase_28 AC 155 ms
17,272 KB
testcase_29 AC 767 ms
17,216 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

import std;

void main () {
    int N, M; readln.read(N, M);
    auto e = readln.split.to!(int[]);
    auto v = new int[](M);
    auto w = new int[](M);
    foreach (i; 0..M) {
        readln.read(v[i], w[i]);
    }

    // 複数のナップサックに適切な部分集合を割り当てていく -> 例のO(3^M)チャンス
    auto cost = new long[](1 << M);
    auto value = new long[](1 << M);
    foreach (S; 0..(1 << M)) {
        long co = 0, va = 0;
        foreach (i; 0..M) if (0 < (S & (1 << i))) {
            co += w[i];
            va += v[i];
        }

        cost[S] = co;
        value[S] = va;
    }

    auto dp = new long[][](N + 1, 1 << M);
    // dp[i][S] := 最初のi個のナップサックに割り当てる部分集合を確定させて、未使用アイテムがSの状態における最大価値

    auto prev = new int[][](N + 1, 1 << M);
    foreach (p; prev) p[] = -1;

    foreach (d; dp) d[] = -long.max;
    dp[0][(1 << M) - 1] = 0;

    foreach (i; 0..N) {
        foreach (S; 0..(1 << M)) {
            if (dp[i][S] == -long.max) continue;
            // 非空部分集合の列挙
            for (uint sub = S; 0 < sub; sub = (sub - 1) & S) {
                if (cost[sub] <= e[i]) {
                    if (dp[i + 1][S ^ sub] < dp[i][S] + value[sub]) {
                        dp[i + 1][S ^ sub] = dp[i][S] + value[sub];
                        prev[i + 1][S ^ sub] = sub;
                    }
                }
            }

            // 取らない
            if (dp[i + 1][S] < dp[i][S]) {
                dp[i + 1][S] = dp[i][S];
                prev[i + 1][S] = 0;
            }
        }
    }

    long ans = 0;
    foreach (d; dp[N]) ans = max(ans, d);

    writeln(ans);

    // 復元
    auto took = new int[](0);
    int cur = N;
    int set = () {
        foreach (S; 0..(1 << M)) if (dp[N][S] == ans) {
            return S;
        }
        assert(0);
    }();

    while (0 < cur) {
        took ~= prev[cur][set];
        set |= prev[cur][set];
        cur--;
    }
    took.reverse;

    foreach (t; took) {
        import core.bitop : popcnt;
        write(popcnt(t));
        foreach (i; 0..M) if (0 < (t & (1 << i))) {
            write(" ", i + 1);
        }
        write("\n");
    }
}

void read (T...) (string S, ref T args) {
    import std.conv : to;
    import std.array : split;
    auto buf = S.split;
    foreach (i, ref arg; args) {
        arg = buf[i].to!(typeof(arg));
    }
}
0