bzoj5359: [Lydsy1805月赛]寻宝游戏
分析
计数Dp什么的果然最烦了。
这道题主要是状态设计很难。
首先我们要把问题转化。
替换可以等价于把经过路径上的x个数不计入答案,不经过路径上的x个数计入答案所获得的最大收益。
然后把这个问题一般化
把经过路径上的k个数不计入答案,不经过路径上的l个数计入答案所获得的最大受益
这样的话原问题是当前问题的一个特殊情况,即
k=l
k
=
l
的情况。
这样的话状态就设计出来了
f[i][j][k][l]
f
[
i
]
[
j
]
[
k
]
[
l
]
表示处理完
(1,1)−>(i,j)
(
1
,
1
)
−
>
(
i
,
j
)
路径内不计入k个数,路径外计入l个数的最大收益。
考虑转移。
往右走一步,意味着
(i,j+1)
(
i
,
j
+
1
)
被选入路径之中。
这个时候就有两种决策:把
a[i][j+1]
a
[
i
]
[
j
+
1
]
计入答案,或者不计入。
同时,往右走一步也意味着
([i+1,n],j)
(
[
i
+
1
,
n
]
,
j
)
上的数都不可能作为路径内的数计入答案,于是它们有可能成为路径外的数计入答案。
这个时候可以枚举
t∈[0,n−i]
t
∈
[
0
,
n
−
i
]
,表示这个路径上选
t
t
个数放到路径外的数中。
这个时候可以简单贪心得到,我们取前大的数是最优的,为了方便,定义
g[i][j][t]
g
[
i
]
[
j
]
[
t
]
表示
([i+1,n],j)
(
[
i
+
1
,
n
]
,
j
)
中的前
t
t
大数之和。
于是得到转移方程
f[i][j][k][l]+g[i][j][t]−>f[i+1][j][k+1][l+t]
f
[
i
]
[
j
]
[
k
]
[
l
]
+
g
[
i
]
[
j
]
[
t
]
−
>
f
[
i
+
1
]
[
j
]
[
k
+
1
]
[
l
+
t
]
类似定义
h
h
数组,转移向下走的方程,最后的答案就是
具体看代码。
计数Dp的状态设计真的很重要。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mem(x, y) memset(x, y, sizeof(x))
#define rep(i, j, k) for(int i = j; i <= k; ++i)
const int N = 55, M = 22;
int ri() {
char ch = getchar(); int x = 0;
for(;ch < '0' || ch > '9'; ch = getchar()) ;
for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
return x;
}
int f[N][N][M][M], g[N][N][N], h[N][N][N], a[N][N], x[N], n, m, K;
bool cmp(int x, int y) {return x > y;}
void Up(int &a, int b) {a = std::max(a, b);}
void Pre() {
n = ri(); m = ri(); K = ri();
rep(i, 1, n) rep(j, 1, m) a[i][j] = ri();
mem(g, 0); mem(h, 0); mem(f, 0x80);
for(int i = 1, tp = 0;i <= n; ++i, tp = 0)
for(int j = m; j; --j) {
std::sort(x + 1, x + tp + 1, cmp);
for(int k = 1; k <= tp; ++k) g[i][j][k] = g[i][j][k - 1] + x[k];
x[++tp] = a[i][j];
}
for(int j = 1, tp = 0;j <= m; ++j, tp = 0)
for(int i = n; i; --i) {
std::sort(x + 1, x + tp + 1, cmp);
for(int k = 1; k <= tp; ++k) h[i][j][k] = h[i][j][k - 1] + x[k];
x[++tp] = a[i][j];
}
f[1][1][0][0] = a[1][1]; f[1][1][1][0] = 0;
}
void Dp() {
rep(i, 1, n) rep(j, 1, m) rep(k, 0, K) rep(l, 0, K) {
rep(t, 0, std::min(K - l, m - j)) //Down
Up(f[i + 1][j][k][l + t], f[i][j][k][l] + g[i][j][t] + a[i + 1][j]),
Up(f[i + 1][j][k + 1][l + t], f[i][j][k][l] + g[i][j][t]);
rep(t, 0, std::min(K - l, n - i)) //Right
Up(f[i][j + 1][k][l + t], f[i][j][k][l] + h[i][j][t] + a[i][j + 1]),
Up(f[i][j + 1][k + 1][l + t], f[i][j][k][l] + h[i][j][t]);
}
int ans = 0;
for(int i = 0; i <= K; ++i) Up(ans, f[n][m][i][i]);
printf("%d\n", ans);
}
int main() {
for(int T = ri(); T--;) Pre(), Dp();
return 0;
}