【洛谷P1896【SCOI2005】】互不侵犯King
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
输入输出格式
输入格式:
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式:
所得的方案数
输入输出样例
输入样例#1:
3 2
输出样例#1:
16
算法:
状压DP
分析:
这道题乍眼一看以为是搜索,其实不然,这道题实则是一道动规的题目。
虽然这道题目看上去和corn fields 求的东西不一样,CF那道题求的是方案数,这里求的是可行性的最大数,但是都是一堆的01串构成的(放与不放)。所以为了优化dp,这道题还是用状压dp。
同样的还是要初始化所有可行的状态和第一行的信息。这个很好理解,也不多说了。
但是这道题没这么简单,除了要记录上一维可行的状态之外,还要记录King的个数,将King也作为一个状态考虑。
上代码:
1 #include<cstdio> 2 #include<iostream> 3 #define C continue 4 using namespace std; 5 6 int n,k; 7 long long dp[10][15000][80],king[15000],state[15000],tot,ans; 8 //dp[i][j][k]表示第i行选j状态在这一行及之前摆上k个国王的方案总数,state是状态,King是那一行的国王总数 9 10 inline void init() //初始化 11 { 12 int i; 13 tot=(1<<n)-1; 14 for (i=0;i<=tot;i++) 15 if (!((i<<1)&i)) //满足提议(同行) 16 { 17 state[++ans]=i; 18 int t=i; 19 while (t) //记录国王个数 20 king[ans]+=t%2,t>>=1; //注意是右移一位 21 } 22 } 23 24 int main() 25 { 26 int i,j,p,s; 27 scanf("%d%d",&n,&k); 28 init(); 29 for (i=1;i<=ans;i++) //初始化第一行 30 if (king[i]<=k) 31 dp[1][i][king[i]]=1; 32 for (i=2;i<=n;i++) //枚举行 33 for (j=1;j<=ans;j++) //枚举这行方案 34 for (p=1;p<=ans;p++) //枚举上行方案 35 { 36 if (state[j]&state[p]) 37 C; 38 if (state[j]&(state[p]<<1)) 39 C; 40 if ((state[j]<<1)&state[p]) 41 C; 42 for (s=1;s<=k;s++) //枚举国王个数 43 { 44 if (king[j]+s>k) 45 C; 46 dp[i][j][king[j]+s]+=dp[i-1][p][s]; 47 } 48 } 49 tot=0; 50 for (i=1;i<=n;i++) 51 for (j=1;j<=ans;j++) 52 tot+=dp[i][j][k]; 53 printf("%lld",tot); 54 return 0; 55 }
状压dp的题主要要考虑如何优化状态,主要都是用01串来解决,其他的内容和普通dp基本是一样的。
嗯,就这样了。