BZOJ1087&&洛谷P1869[SCOI2005]互不侵犯

人生中的第一道状压dp(照着题解写的(捂脸))

先dfs预处理出每种情况下的二进制(状压不是用01描轮廓吗),然后枚举每行的选哪个,不用考虑他的上一行,因为这行是从上一行转移下来的,我们只需要判断和下一行冲不冲突,不冲突就累加答案

初始化时第一行的美种情况的方案数都是1,最后去最后一层累加答案

代码(从头到尾抄了一遍,虽然不是直接交的题解,但是还是好羞耻(捂脸))

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=2050;
long long int f[10][M][100];
int push[M],get[M];
int n,m,cnt=0;
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void dfs(int emm,int sum,int x)
{
	if (x>=n){get[++cnt]=emm;push[cnt]=sum;return ;}//到了最底层 
	dfs(emm,sum,x+1);//不选当前点,那么找它下一个 
	dfs(emm+(1<<x),sum+1,x+2);//选了当前点,那么下一个不能选,跳过 
	return ;
}//处理每种情况 
int main()
{
	n=read();m=read();
	dfs(0,0,0);
	for (int i=1;i<=cnt;i++) 
		f[1][i][push[i]]=1;//第一层的每种方案都是一种 
	for (int i=2;i<=n;i++)
		for (int j=1;j<=cnt;j++)
			for (int k=1;k<=cnt;k++)
				if (get[j]&get[k]) continue;//冲突 
					else if ((get[j]<<1)&get[k]) continue;//冲突 
						else if (get[j]&(get[k]<<1)) continue;//冲突 
							else for(int e=m;e>=push[j];e--) f[i][j][e]+=f[i-1][k][e-push[j]];//累加 
	long long int ans=0;
	for (int i=1;i<=cnt;i++) ans+=f[n][i][m];//在最底层统计答案 
	cout<<ans;
	return 0; 
}

猜你喜欢

转载自blog.csdn.net/acerandaker/article/details/80915201