[NOIP2007 提高组] 矩阵取数游戏——区间dp,__int128
事打开洛谷题库就会映入眼帘的一道题。(P1005)
显然每一行是相互独立的,只需要分别求出答案再加起来。
如果问题不好下手,不妨采取逆推的思路:倒着取,先任意取一个,然后每次从已取区间左边或右边取一个,第i次的得分是\(2^{m-i+1}\)。显然,我们可以用区间DP来解决。
状态:设\(f_{i,j}\)是区间\([i,j]\)的答案
初值:显然\(f_{i,i}=a_{i} \times 2\)
转移:区间长度大于1时,$f_{i,j} = 2 \times max( f_{i-1,j} + a_{i} ,f_{i,j-1} + a_{j}) $
__int128需要手写io,模板见代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define int __int128
int n, m, ans, f[110][110], a[110][110];
int read() {
int s = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') f = -1;
ch = getchar();
}
while (isdigit(ch)) s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
void solve(int a[]) {
memset(f, 0, sizeof(f));
for (int k = 0; k < m; k++)
for (int i = 1; i + k <= m; i++)
f[i][i+k] = 2 * std::max(f[i+1][i+k] + a[i], f[i][i+k-1] + a[i+k]);
ans += f[1][m];
}
signed main() {
n = read(); m = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) a[i][j] = read();
for (int i = 1; i <= n; i++) solve(a[i]);
if (!ans) puts("0");
else print(ans);
return 0;
}