题目描述
有一个(n imes m)的网格图,其中某些格子有财宝,每次从左上角出发,只能向下或右走。问至少走多少次才能将财宝捡完。
此对此问题变形,假设每个格子中有好多财宝,而每一次经过一个格子至多只能捡走一块财宝,至少走多少次才能把财宝全部捡完。
(n,mleq 1000)
题解
定义偏序关系
把一个格子拆成很多个点,每个点代表一个财宝。
对于两个点(a,b),称(a<b)当且仅当(a)能走到(b)
那么这道题求的是最小链覆盖
根据Dilworth定理,最小链覆盖数(=)最长反链长度。
直接DP就行了。
设(f_{i,j})为以((i,j))为结尾的最长反链长度
[f_{i,j}=a_{i,j}+max_{k>i,l<j}f_{k,l}
]
时间复杂度:(O(nm))
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int s[1010][1010];
int f[1010][1010];
int a[1010][1010];
void solve()
{
int n,m;
scanf("%d%d",&n,&m);
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
memset(s,0,sizeof s);
memset(f,0,sizeof f);
int ans=0;
for(i=n;i>=1;i--)
for(j=1;j<=m;j++)
{
s[i][j]=max(s[i][j],s[i][j-1]);
s[i][j]=max(s[i][j],s[i+1][j]);
f[i][j]=s[i][j]+a[i][j];
ans=max(ans,f[i][j]);
s[i-1][j+1]=f[i][j];
}
printf("%d
",ans);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
solve();
return 0;
}