【BZOJ3997】【TJOI2015】组合数学 Dilworth定理 DP

题目描述

  有一个\(n\times m\)的网格图,其中某些格子有财宝,每次从左上角出发,只能向下或右走。问至少走多少次才能将财宝捡完。

  此对此问题变形,假设每个格子中有好多财宝,而每一次经过一个格子至多只能捡走一块财宝,至少走多少次才能把财宝全部捡完。

  \(n,m\leq 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\n",ans);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
		solve();
	return 0;
}
posted @ 2018-03-06 15:29  ywwyww  阅读(211)  评论(0编辑  收藏  举报