状压DP

套路

其实套路挺明显的,也很容易看出来要用状压,所以通常就把一行的状态压缩成一个数(一般适用于放不放棋子这类的只有选/不选两种状态的)
(有可能会有特殊情况,比如炮兵阵地就是三进制状压,这个我们先不管)
核心代码大概长这样,根据不同题的不同限制改一改就行了:

void dp(){
	f[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<sum;j++){//< is rignt,<= is wrong,because 2^n-1 means every figure is 1 in binary system
			if((j>num[i])||((j&num[i])!=j)||(j&(j<<1))||(j&(j>>1))) continue;
			for(int k=0;k<sum;k++){
				if(!(k&j)){
					f[i][j]=(f[i][j]+f[i-1][k])%mod;
				}
			}
		}
	}
}

例题

P1879 Corn Fields G

题目
code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define maxn 20
#define ll long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

const int mod=100000000;
int m,n,a[maxn][maxn];
int num[maxn],f[maxn][1<<maxn],sum,ans;

void dp(){
	f[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<sum;j++){//< is rignt,<= is wrong,because 2^n-1 means every figure is 1 in binary system
			if((j>num[i])||((j&num[i])!=j)||(j&(j<<1))||(j&(j>>1))) continue;
			for(int k=0;k<sum;k++){
				if(!(k&j)){
					f[i][j]=(f[i][j]+f[i-1][k])%mod;
				}
			}
		}
	}
}

int main(){
	read(m),read(n);
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			read(a[i][j]);
			num[i]=(num[i]<<1)+a[i][j];
		}
	}
	sum=1<<n;
	dp();
	for(int i=0;i<sum;i++) (ans+=f[m][i])%=mod;
	cout<<ans<<endl;
	return 0;
} 

P1896 互不侵犯

题目
因为限制了k个国王,所以f数组要多开一维记一下国王数量
code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define maxn 15
#define maxm 200
#define ll long long//不开long long要出锅啊 
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int n,k;
ll ans,king_yihang,cnt,a[maxm],num[maxm],f[maxn][maxm][maxm];//第i行第j种状态放了k个国王 

void dp(){
	f[0][1][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=cnt;j++){
			for(int t=0;t<=k;t++){
				if(num[j]>t) continue;
				for(int u=1;u<=cnt;u++){
					if((a[u]&a[j])||(a[u]&(a[j]<<1))||(a[u]&(a[j]>>1))) continue;
					f[i][j][t]+=f[i-1][u][t-num[j]];
				}
			}
		}
	} 
}

int main(){
	read(n),read(k);
	for(int i=0;i<=(1<<n)-1;i++){
		king_yihang=0;
		if(i&(i<<1)) continue;
		for(int j=0;j<=n-1;j++) if(i&(1<<j)) king_yihang++;
		a[++cnt]=i;
		num[cnt]=king_yihang;
	}
	dp();
	for(int i=1;i<=cnt;i++) ans+=f[n][i][k];
	cout<<ans<<endl;
	return 0;
}

P4163 排列

题目
code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define maxn 1010
#define ll long long
using namespace std;
template<typename T>
inline void read(T &x){
	x=0;bool flag=0;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
	if(flag) x=-x;
}

int T,d,len,num[20],f[1024][maxn];
char s[20];
bool vis[20];

int main(){
	read(T);
	for(int t=1;t<=T;t++){
		memset(s,0,sizeof(s));
		memset(num,0,sizeof(num));
		memset(f,0,sizeof(f));
		cin>>s+1;read(d);
		len=strlen(s+1);
		for(int i=1;i<=len;i++) num[i]=s[i]-'0';
		f[0][0]=1;
		for(int i=0;i<(1<<len)-1;i++){
			memset(vis,0,sizeof(vis));
			for(int j=1;j<=len;j++){
				if((i&(1<<(j-1)))||vis[num[j]]) continue;
				vis[num[j]]=1;
				for(int k=0;k<d;k++){//********
					f[i|(1<<(j-1))][(10*k+num[j])%d]+=f[i][k];
				}
			}
		}
		cout<<f[(1<<len)-1][0]<<endl;
	}
	return 0;
} 

P2150 寿司晚宴

题目
没写呢,写完再来填坑

原文地址:https://www.cnblogs.com/DReamLion/p/14811275.html