【APIO2007】动物园(状态压缩dp)

首先这题的N和M都非常大,对它们状压是不可能的。因为每个位置只能看到5个,所以不妨对每个位置进行状压,每个位置用一个5位的二进制数表示这个位置所看到的5个格子[i,i+4]的安排方式。

那么首先在输入时计算出每个位置每种状态能使多少人高兴,用cnt[i,S]记录。

***注意输入的位置并不是在五个格子中间,不同于给的图,而是在五个格子的起点(巨坑...)。

接下来推一下状态转移方程。为了方便转移,我们让i号格子所管的前四个格子[i,i+3],由i-1号格子所管的后四个格子[i,i+3]转移而来,如下图:

 

这里有一点位运算的知识,(S&15)是为了把S的二进制第五位丢掉,“<<1”是为了往后移一位,让第一位空出来,决定第i-1位是否移走(“?”处)。

此时,不论i-1位移走与否,最终S是确定的(比如上图的10101确定,只是前面的“?1010”要取最大),前后格每个状态是完全独立的,那么只需在末尾加上一个 cnt[i][S]即可。

于是最终的状态转移方程:

\small f[i][S]=max\left \{ f[i-1][(S\&15)<<1],f[i-1][(S\&15)<<1|1] \right \}+cnt[i][S]

还有一个细节,由于是一个环,i=1时,需要得到它的初始状态,可以通过枚举f[0]的S完成。

f[N]做完后相当于回到了f[0],所以状态必须与f[0]一致,ans就取f[N][S<<1],f[N][S<<1|1]里的最大值。

 

#include<bits/stdc++.h>
using namespace std;
const int MAXN=10005;
int N,M,cnt[MAXN][(1<<6)],f[MAXN][(1<<6)];

char c;
void scan(int &x)
{
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}

int main()
{
	int i,j,fear,like,x,pos,S;
	scan(N);scan(M);
	for(i=1;i<=M;i++)
	{
		scan(pos);scan(fear);scan(like);
		int t1=0,t2=0;
		
		while(fear--)
		{
			scan(x);
			x=(x+N-pos)%N;
			t1|=(1<<x);
		}
		
		while(like--)
		{
			scan(x);
			x=(x+N-pos)%N;
			t2|=(1<<x);
		}
		
		for(j=0;j<32;j++)
			if((j&t2)!=0 || ((31^j)&t1)!=0)
				cnt[pos][j]+=1;
	}
	
	int ans=0;
	for(S=0;S<16;S++) //枚举起始 
	{
		for(j=0;j<32;j++) f[0][j]=-0x3f3f3f3f;  //足够小就行 
		f[0][S<<1]=f[0][S<<1|1]=0;
		
		for(i=1;i<=N;i++)
			for(j=0;j<32;j++)
				f[i][j]=max(f[i-1][(j&15)<<1],f[i-1][(j&15)<<1|1])+cnt[i][j];
		
		ans=max(ans,max(f[N][S<<1],f[N][S<<1|1]));
	}
	cout<<ans;
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/82150260
今日推荐