P2331 [SCOI2005]最大子矩阵 题解

这个题实际上只需要考虑m=1和m=2的情况。

对于\(m=1\)时,实际上就是一个线性dp,设\(f(i,j,0/1)\)表示当前dp到第\(i\)位,已经使用了\(j\)个矩阵,当前取/不取的分值。

转移很简单。

\(f[i][j][0]=max(f[i-1][j][1],f[i-1][j][0])\)

\(f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0])+a[i][1]\)


对于m=2时,我们可以考虑举出当前矩阵的所有情况。

我们显然可以发现以下几种情况:(0表示不取,1表示取)

\(01 \quad 10 \quad 00 \quad 11\)

我们考虑以下这样的数据

-1 1
1 1
1 -1

这样做肯定就不行了,我们还需要记录一个新的状态表示两个都选但是是分开选。

由此我们可以进行转移。

转移很简单,考虑上一行是哪种情况即可

具体可以看代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define orz cout<<"lyakioi!!!!!!!!!!!!!!!!!"<<endl
inline int r(){int s=0,k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){s=s*10+c-'0';c=getchar();}return s*k;}
int ans,n,a[205][205],m,t,f[1005][101][5];//0:00  1:01  2:10  3:11合并  4:11分开 
signed main()
{
	memset(f,128,sizeof(f));
	n=r();m=r();t=r();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	a[i][j]=r();
	
	if(m==1)
	{
		f[0][0][0]=0;
		for(int i=1;i<=n;i++)
		for(int j=0;j<=t;j++)
		{
			f[i][j][0]=max(f[i-1][j][1],f[i-1][j][0]);
			if(j)f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0])+a[i][1];
		}
		cout<<max(f[n][t][1],f[n][t][0]);
	}
	else
	{
		
		f[0][0][0]=0;
		
		for(int i=1;i<=n;i++)
		for(int k=0;k<=t;k++)
		{
			for(int l=0;l<=4;l++)//啥都不选 
			f[i][k][0]=max(f[i][k][0],f[i-1][k][l]);
			
			f[i][k][1]=max(f[i][k][1],f[i-1][k][1]);
			f[i][k][1]=max(f[i][k][1],f[i-1][k][4]);
			if(k)f[i][k][1]=max(f[i][k][1],f[i-1][k-1][0]);
			if(k)f[i][k][1]=max(f[i][k][1],f[i-1][k-1][2]);
			if(k)f[i][k][1]=max(f[i][k][1],f[i-1][k-1][3]);
			f[i][k][1]+=a[i][2];
			
			f[i][k][2]=max(f[i][k][2],f[i-1][k][2]);
			f[i][k][2]=max(f[i][k][2],f[i-1][k][4]);
			if(k)f[i][k][2]=max(f[i][k][2],f[i-1][k-1][0]);
			if(k)f[i][k][2]=max(f[i][k][2],f[i-1][k-1][1]);
			if(k)f[i][k][2]=max(f[i][k][2],f[i-1][k-1][3]);
			f[i][k][2]+=a[i][1];
	
			f[i][k][3]=max(f[i][k][3],f[i-1][k][3]);
			if(k)f[i][k][3]=max(f[i][k][3],f[i-1][k-1][0]);
			if(k)f[i][k][3]=max(f[i][k][3],f[i-1][k-1][1]);
			if(k)f[i][k][3]=max(f[i][k][3],f[i-1][k-1][2]);
			if(k)f[i][k][3]=max(f[i][k][3],f[i-1][k-1][4]);
			
			f[i][k][4]=max(f[i][k][4],f[i-1][k][4]);
			if(k>1)f[i][k][4]=max(f[i][k][4],f[i-1][k-2][0]);
			if(k)f[i][k][4]=max(f[i][k][4],f[i-1][k-1][1]);
			if(k)f[i][k][4]=max(f[i][k][4],f[i-1][k-1][2]);
			if(k>1)f[i][k][4]=max(f[i][k][4],f[i-1][k-2][3]);
			
			f[i][k][3]+=a[i][1]+a[i][2];
			f[i][k][4]+=a[i][1]+a[i][2];
		}
		for(int i=0;i<=4;i++)
		ans=max(ans,f[n][t][i]);
		cout<<ans;
	}
	
}


我们可以继续想想m更大\((<6)\)的情况。
那么我们就只能进行状压动规了,但是也不能用裸的状压动规
考虑下面这样一种状态:
\(001111100\)
我们依然不知道它是连起来还是分开的
所以我们至少需要使用三进制:
\(001122200\)
这真的很难写)

posted @ 2021-07-24 16:03  lei_yu  阅读(35)  评论(0编辑  收藏  举报