Typical DP Contest - L 猫

L: 猫 - Typical DP Contest | AtCoder

  • 普通に dp すると TLE するが式変形をすると間に合うタイプ
class l_cat {
public:
    void solve(void) {
            int N;
            cin>>N;

            vector<vector<int>> f(N, vector<int>(N,0));
            REP(i, N)
            REP(j, N)
                cin>>f[i][j];

            //
            // dp[i][j] := 猫iまで並べたとき、i番目の猫から距離1以内にいる猫のうち
            //             最も小さい番号がjであるようなときの幸福度の総和の最大値
            //
            // dp[i+1][j] のとき、 j<=l<=i+1 は i+1 と 1 以内の位置にあるので
            //                              i+1
            // dp[i+1][j] = max{dp[i][k] + 2*∑ f[i+1][l] | k <=j }
            //                              l=j
            //
            // (dp[i][i] = max{dp[i-1][k] | k <= i-1} であることに注意)
            //
            // となる。 (2 倍なのは猫 i,j 双方からの親密度を計算するため)
            //
            // このままだと更新に O(N^3) かかってしまうが
            //
            //                i
            //  Sum[i][j] = 2*∑ f[i][l] とすると
            //               l=j
            //
            // dp[i+1][j] = max{dp[i][k] + Sum[i+1][j] | k <= j }
            //            = max{max{dp[i][k] | k<=j-1}, dp[i][j]} + Sum[i+1][j]
            //            = max{max{dp[i][k] + Sum[i+1][j-1] - Sum[i+1][j-1] | k <= j-1 },
            //                  dp[i][j]} + Sum[i+1][j]
            //            = max{dp[i+1][j-1]-Sum[i+1][j-1], dp[i][j]} + Sum[i+1][j]
            //
            // 計算量は O(N^2)
            //
            const int inf = (1<<30);
            vector<vector<ll>> sum(N+1,vector<ll>(N+1,0));
            vector<vector<ll>>  dp(N+1,vector<ll>(N+1,-inf)); // f[i][j] < 0 なるケースもあるので -inf で初期化
            dp[0][0] = 0;

            // 和を事前計算しておく
            REP(i, N)
            for (int j = i; j >= 0; --j)
                sum[i][j] = sum[i][j+1] + 2*f[i][j];

            REP(i, N)
            for (int j = 0; j <= i+1; ++j) // dp[i][i] も計算する
            {
                dp[i+1][j] = dp[i][j];
                if (j >= 1)
                    dp[i+1][j] = max(dp[i+1][j], dp[i+1][j-1]-sum[i+1][j-1]);
                dp[i+1][j] += sum[i+1][j];
            }

            ll res = 0;
            REP(i, N)
                res = max(res, dp[N-1][i]);
            cout<<res<<endl;
    }
};