dp[どれを最後に拾った][最後に足したK] = 最大
i 番目の次に j 番目を拾おうとする場合、最小でも、
k + (k + 1) + (k + 2) + ... (k + (j - k)) = (j - k) * k + ((k + 1) * k / 2)
だけは足す必要がある。それ以上であれば、移動可能ということになる。
あとはいつもどおり DP する。
const int N = 50 + 1; const int M = 100000 + 1; int dp[N][M]; class BaronsAndCoins { public: int getMaximum(vector <int> x, vector <int> y) { x.push_back(0); y.push_back(0); const int n = x.size(); { vector< pair<int, int> > ord; for (int i = 0; i < n; ++i) { ord.push_back(make_pair(y[i], x[i])); } sort(ord.begin(), ord.end()); for (int i = 0; i < n; ++i) { y[i] = ord[i].first; x[i] = ord[i].second; } } fill(&dp[0][0], &dp[N - 1][M - 1] + 1, -(1 << 28)); dp[0][0] = 0; for (int curr = 0; curr < n; ++curr) { for (int k = 0; k < M; ++k) { if (dp[curr][k] < 0) continue; for (int next = curr + 1; next < n; ++next) { unless (y[curr] < y[next]) continue; int turn = y[next] - y[curr]; int mn = (k * turn) + (turn * (turn + 1)) / 2; if (x[curr] + mn <= x[next]) { int m = x[next] - (x[curr] + mn); int small = (m / turn) + (bool)(m % turn); int next_k = k + small + turn; if (next_k < M) { dp[next][next_k] = max(dp[next][next_k], dp[curr][k] + 1); } } } } } return *max_element(&dp[0][0], &dp[N - 1][M - 1] + 1); } };