【BZOJ】1087: [SCOI2005]互不侵犯King(状压dp)
http://www.lydsy.com:808/JudgeOnline/problem.php?id=1087
状压dp是第一次写啊,我也是才学TAT。状压dp一般都用一个值表示集合作为dp的一个状态,然后根据集合和dp的性质转移。通常用于啥啥啥。。。。。
我引用些吧
我们知道,用DP解决一个问题的时候很重要的一环就是状态的表示,一般来说,一个数组即可保存状态。但是有这样的一些题目,它们具有DP问题的特性,但是 状态中所包含的信息过多,如果要用数组来保存状态的话需要四维以上的数组。于是,我们就需要通过状态压缩来保存状态,而使用状态压缩来保存状态的DP就叫 做状态压缩DP。
回到此题:
设状态f[i][j][k]表示前i行放j个国王且在第i行放置国王的情况为k时的方案数
有
f[i][j][k]=sum{ f[i-1][j-cnt[x]][x] | x为所有不与k互斥的放置情况,cnt[x]为放置为x时国王的数量 }
#include <cstdio> #include <cstring> #include <string> #include <iostream> #include <cmath> #include <algorithm> using namespace std; #define rep(i, n) for(int i=0; i<(n); ++i) #define for1(i,a,n) for(int i=(a);i<=(n);++i) #define for2(i,a,n) for(int i=(a);i<(n);++i) #define for3(i,a,n) for(int i=(a);i>=(n);--i) #define for4(i,a,n) for(int i=(a);i>(n);--i) #define CC(i,a) memset(i,a,sizeof(i)) #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define read(a) a=getnum() #define print(a) printf("%d", a) inline int getnum() { int ret=0; char c; for(c=getchar(); c<'0' || c>'9'; c=getchar()); for(; c>='0' && c<='9'; c=getchar()) ret=ret*10+c-'0'; return ret; } long long f[10][82][512], ans; bool c1[512], c2[512][512]; int cnt[512], n, m, bit; void init() { bit=(1<<n)-1; int s, t; for1(i, 0, bit) if((i&(i>>1))==0) { t=i; s=0; while(t) { if(t&1) ++s; t>>=1; } cnt[i]=s; c1[i]=true; } for1(i, 0, bit) if(c1[i]) for1(j, 0, bit) if(c1[j]) if((i&(j>>1))==0 && (j&(i>>1))==0 && (i&j)==0) c2[i][j]=true; } int main() { read(n); read(m); init(); int p; for1(i, 0, bit) if(c1[i]) f[1][cnt[i]][i]=1; for1(i, 2, n) for1(j, 0, bit) if(c1[j]) for1(k, 0, bit) if(c1[k]) if(c2[j][k]) { for(p=cnt[k]; p+cnt[j]<=m; ++p) f[i][p+cnt[j]][j]+=f[i-1][p][k]; } for1(i, 0, bit) ans+=f[n][m][i]; printf("%lld", ans); return 0; }
Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
HINT
Source
博客地址:www.cnblogs.com/iwtwiioi 本文为博主原创文章,未经博主允许不得转载。一经发现,必将追究法律责任。