yukicoder No.324 - 落ちてた閉路グラフ

解法

動的計画法です.
dp[i+1][v\_num][pre\_used][top\_used]:=(i番目の頂点まで見ていて, 選んだ頂点の数がv\_num, 直前の頂点を選んだかどうかがpre\_used, 最初の頂点を選んだかどうかがtop\_used, このときのコストの最大値)とします.
これを用いてn番目までの頂点まで見た後, pre\_usedtop\_usedがともに真であるような状態については, w[n-1]の辺のコストを加えることになります. このように最後の補正を行った後に, 計算した値より最大コストを求めればよいです.

ソースコード

#include <iostream>
using namespace std;

const int kNEG_INF = -(1 << 28);
const int kMAX_N = 3010;
const int kMAX_M = 3010;

int n, m;
int w[kMAX_N];

int dp[2][kMAX_M][2][2];  // dp[i+1][v_num][pre_used][top_used]

void Initialize(int arr[][2][2]) {
    for (int i = 0; i <= m; i++) for (int j = 0; j <= 1; j++)
        for (int k = 0; k <= 1; k++) arr[i][j][k] = kNEG_INF;
}

void Solve() {
    // dp
    Initialize(dp[0]);
    dp[0][0][0][0] = 0;
    for (int v_i = 0; v_i < n; v_i++) {
        Initialize(dp[(v_i + 1) % 2]);
        for (int v_num = 0; v_num <= m; v_num++) {
            for (int pre_used = 0; pre_used <= 1; pre_used++) {
                for (int top_used = 0; top_used <= 1; top_used++) {
                    if (dp[v_i % 2][v_num][pre_used][top_used] <= kNEG_INF) continue;
                    // 使わない
                    dp[(v_i + 1) % 2][v_num][false][top_used] = max(dp[(v_i + 1) % 2][v_num][false][top_used],
                                                                     dp[v_i % 2][v_num][pre_used][top_used]);
                    if (v_num + 1 > m) continue;

                    // 使う
                    if (pre_used) { // 辺のコストが追加
                        dp[(v_i + 1) % 2][v_num + 1][true][top_used | (v_i == 0)] = max(dp[(v_i + 1) % 2][v_num + 1][true][top_used | (v_i == 0)],
                                                                                        dp[v_i % 2][v_num][pre_used][top_used] + w[(v_i - 1 + n) % n]);
                    } else {    // 辺のコストは増えない
                        dp[(v_i + 1) % 2][v_num + 1][true][top_used | (v_i == 0)] = max(dp[(v_i + 1) % 2][v_num + 1][true][top_used | (v_i == 0)],
                                                                                        dp[v_i % 2][v_num][pre_used][top_used]);
                    }
                }
            }
        }
    }
    int ans = kNEG_INF;
    for (int pre_used = 0; pre_used <= 1; pre_used++) {
        for (int top_used = 0; top_used <= 1; top_used++) {
            ans = max(ans, dp[n % 2][m][pre_used][top_used] + (pre_used && top_used ? w[n - 1] : 0));
        }
    }
    cout << ans << endl;
}

int main() {
    cin >> n >> m;

    for (int i = 0; i < n; i++) {
        cin >> w[i];
    }

    Solve();

    return 0;
}

感想

ちょうどいい難易度でした.