[Bzoj5359][Lydsy1805月赛]寻宝游戏(dp)
Posted lzdhydzzh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Bzoj5359][Lydsy1805月赛]寻宝游戏(dp)相关的知识,希望对你有一定的参考价值。
5359: [Lydsy1805月赛]寻宝游戏
Time Limit: 2 Sec Memory Limit: 512 MB
Submit: 71 Solved: 19
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
Sample Output
HINT
Source
如果k = 0,就是做一遍O(nm)的经典dp
k > 0 :定义状态f[i][j][k][l],表示已考虑完以(1,1)为左上角,(i,j)为右下角的矩形,一条(1,1)到(i,j)的路径中有k个点不选,从外面选了l个点的最大价值。
预处理出g[i][j][k]((i,j + 1)~(i,m)中前k大之和),h[i][j][k] ((i + 1,j) ~(n,j)中前k大和),复杂度O(n^3logn)
转移起来就很简单了(记录每个点权值为s[i][j]):
f[i][j][k][l] + g[i][j][x] + s[i + 1][j] ----> f[i+1][j][k][l + x] (从右选了前x大,往下走)
f[i][j][k][l] + g[i][j][x] ----> f[i+1][j][k + 1][l + x] (从右选了前x大,往下走,且舍弃s[i + 1][j])
f[i][j][k][l] + h[i][j][x] + s[i][j + 1] ----> f[i][j + 1][k][l + x] (从下选了前x大,往右走)
f[i][j][k][l] + h[i][j][x] ----> f[i][j + 1][k + 1][l + x] (从下选了前x大,往右走,且舍弃s[i][j + 1])
初始化f[1][1][0][0] = s[1][1],f[1][1][1][0] = 0。就可以在O(n ^ 2k^3)转移了
最后答案就为max(f[n][m][t][t]),t∈[0,K]
(所以说dp定义状态比转移关键呢)
AC代码:
# include <iostream> # include <cstdio> # include <cstring> # include <algorithm> using namespace std; int f[52][52][22][22],a[52],b[52],s[52][52],g[52][52][52],h[52][52][52],n,m,K,dt; bool cmp(int x,int y){return x > y;} void init() { memset(g,0,sizeof g); memset(h,0,sizeof h); for(int i = 1;i <= n;i++) { dt = 0; for(int j = m;j >= 1;j--) { sort(a + 1,a + dt + 1,cmp); for(int k = 1;k <= dt;k++)b[k] = b[k - 1] + a[k],g[i][j][k] = b[k]; a[++dt] = s[i][j]; } } for(int j = 1;j <= m;j++) { dt = 0; for(int i = n;i >= 1;i--) { sort(a + 1,a + dt + 1,cmp); for(int k = 1;k <= dt;k++)b[k] = b[k - 1] + a[k],h[i][j][k] = b[k]; a[++dt] = s[i][j]; } } memset(f,0x80,sizeof f); f[1][1][0][0] = s[1][1]; f[1][1][1][0] = 0; } int main() { int Case;scanf("%d",&Case); while(Case--) { scanf("%d %d %d",&n,&m,&K); for(int i = 1;i <= n;i++) for(int j = 1;j <= m;j++) scanf("%d",&s[i][j]); init(); for(int i = 1;i <= n;i++) for(int j = 1;j <= m;j++) for(int k = 0;k <= K;k++) for(int l = 0;l <= K;l++) { for(int t = 0;t <= min(K - l,m - j);t++) f[i + 1][j][k][l + t] = max(f[i + 1][j][k][l + t],f[i][j][k][l] + s[i + 1][j] + g[i][j][t]), f[i + 1][j][k + 1][l + t] = max(f[i + 1][j][k + 1][l + t],f[i][j][k][l] + g[i][j][t]); for(int t = 0;t <= min(K - l,n - i);t++) f[i][j + 1][k][l + t] = max(f[i][j + 1][k][l + t],f[i][j][k][l] + s[i][j + 1] + h[i][j][t]), f[i][j + 1][k + 1][l + t] = max(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++)ans = max(ans,f[n][m][i][i]); printf("%d\n",ans); } }
以上是关于[Bzoj5359][Lydsy1805月赛]寻宝游戏(dp)的主要内容,如果未能解决你的问题,请参考以下文章