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

题目描述

  有一个(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;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8514598.html