题解:[APIO2007]动物园

传送门

对于这个题目很明显的就是状压DP,不过,作为一个不会状压DP的蒟蒻,考场让只能根据大佬们的之前聊天和对状压DP的印象来瞎搞
状态是这样设计的:

f[i,j] 表示转移到从第i个位置出发的j表示五个的动物的状态能使多少个小朋友满意 s[i,j]
表示从i出发的5个位置中表示j的状态使多少个小朋友满意

所以,状态量其实和小朋友的人数没什么关系,总状态量是n*25 =320 000,两个数组是640 000,16M的空间是够用的。
对于每个小朋友,读入的时候就表示出来两个01串,这里用x和y表示,然后枚举状态,分别和x和y作比较,如果可以,那么s[E][状态]就++。
比较是这样的:

for(int i=0; i<lim; ++i) {
	if((i&x)||(~i&y))	//有害怕的移走了或者喜欢的还存在的状态为i
	/*
		i&x时候,如果有一个和x中同时为1,则true
		~i中1变0,0变1,相当于是移走的就是1,还在的就是0,害怕的只要有移走,那么就是true
	*/
	++s[E][i];	//此状态让小朋友满意
}

预处理结束了,就要开始DP了,同样也是要枚举初始状态,把初始状态作为0,其他为-INF,进行转移。
转移的过程中:

for(re int i=1; i<=n; ++i)	 
	for(re int k=0; k<lim; ++k)
		f[i][k]=max(f[i-1][(k<<1)%lim],f[i-1][((k<<1)+1)%lim])+s[i][k];
		//从上个状态的最大值转移过来后加上此状态这个位置的小朋友满意个数
		/*
			((k<<1)+1)%lim abcde -> bcde1
			(k<<1)%lim abcde -> bcde0
		*/

最后因为饶了一圈,所以回到了最初枚举的状态。
完整代码如下:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define re register
#define gc getchar()
#define ll long long
#define il inline
const int N=10010,lim=(1<<5),INF=1e9+7;
il int read() {
	re int x(0),f(1);
	re char ch=gc;
	while(ch<'0'||ch>'9') {
		if(ch=='-') f=-1;
		ch=gc;
	}
	while(ch>='0'&&ch<='9') {
		x=(x<<1)+(x<<3)+(ch^48);
		ch=gc;
	}
	return x*f;
}
int n,c,f[N][lim],s[N][lim],ans;

int main() {

	n=read(),c=read();

	for(int k=1; k<=c; ++k) {
		int E=read(),F=read(),L=read(),x=0,y=0;
		for(int i=1; i<=F; ++i) {
			int a=read();
			x|=(1<<((a-E+n)%n));
		}
		for(int i=1; i<=L; ++i) {
			int a=read();
			y|=(1<<((a-E+n)%n));
		}
		for(int i=0; i<lim; ++i) {
			if((i&x)||(~i&y))
				++s[E][i];
		}
	}

	for(re int j=0; j<lim; ++j) {
		for(re int i=0; i<lim; ++i)
			f[0][i]=-INF;
		f[0][j]=0;
		for(re int i=1; i<=n; ++i)	 
			for(re int k=0; k<lim; ++k)
				f[i][k]=max(f[i-1][(k<<1)%lim],f[i-1][((k<<1)+1)%lim])+s[i][k];
		ans=max(ans,f[n][j]);
		
	}
	cout<<ans;

	return 0;
}

于是,小蒟蒻的第一次状压DP get

猜你喜欢

转载自blog.csdn.net/weixin_43464026/article/details/87356813