Typical DP Contest L - 猫

http://tdpc.contest.atcoder.jp/tasks/tdpc_cat

問題概要

1~Nまでの番号がついたN匹の猫がいる。任意の猫2匹の組合せには親密度が設定されており、各猫の幸福度は自分との距離が1以内のところにいるすべての猫との親密度の和で定義される。1次元の数直線上の実数座標に猫を番号順に座標が大きくなっていくよう並べたときの各猫の幸福度の総和の最大値を求めよ。

N <= 1000

解法

dp(i, j) = i番目の猫まで置き、i番目の猫と(j~i-1)番目の猫が距離1以内にいるときの最大幸福度

猫は番号順に左から並べるという制約があるので、猫iが距離1以内に含む可能性のある猫の集合は(0~i-1), (1~i-1), ..., i-1, 空集合, というパターンだけである。これらのパターンでの親密度の増え方は普通の累積和で前計算しておけるのでdpの更新時にはO(1)でできる。前処理, DPともにO(N2).

感想

最初あんなに短い問題文を誤読して順番自由と思い込みムズすぎ!!!!!!!!!!!となって解説を漁ってしまいました……

コード (D言語)

import std.stdio, std.array, std.string, std.conv, std.algorithm;
import std.typecons, std.range, std.random, std.math, std.container;
import std.numeric, std.bigint, core.bitop;

void main() {
    auto N = readln.chomp.to!int;
    auto A = N.iota.map!(_ => readln.split.map!(to!long).array).array;
    auto B = new long[][](N + 1, N + 1);
    foreach (i; 0..N) foreach (j; iota(i, -1, -1)) B[i][j] += B[i][j+1] + A[i][j];
    auto dp = new long[][](N + 1, N + 1);

    foreach (i; 0..N) {
        dp[i+1][i+1] = - (1L << 59);
        foreach (j; 0..i+1) {
            dp[i+1][i+1] = max(dp[i+1][i+1], dp[i][j]);
            dp[i+1][j] = dp[i+1][i+1] + B[i+1][j] * 2;
        }
    }

    dp[N].reduce!max.writeln;
}