【洛谷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基本是一样的。

 

嗯,就这样了。

原文地址:https://www.cnblogs.com/Ronald-MOK1426/p/8456798.html