【算法•日更•第十四期】信息奥赛一本通1592:【例 1】国王题解

  废话不多说,直接上题:


1592:【例 1】国王


时间限制: 500 ms         内存限制: 65536 KB
提交数: 290     通过数: 111 

【题目描述】

原题来自:SGU 223

在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。

【输入】

只有一行,包含两个整数 n 和 k。

【输出】

每组数据一行为方案总数,若不能够放置则输出 0。

【输入样例】

3 2

【输出样例】

16

【提示】

样例输入 2

 

4 4

 

样例输出 2

 

79

 

数据范围与提示:

对于全部数据,1≤n≤10,0≤k≤n2 。

【来源】


  不得不说,这道题太像还是n皇后了。详见此题链接:戳这里哦~

  只不过是皇后变成了国王、地图上没有了坑而已。

  不过我们可以先看一看数据规模,我一下子就被惊到了。n只有10,k只有100,但是虽然数据小,但是搜索要真的一个一个搜起来,那可就真的是指数级复杂度,肯定不行。 

  于是我们就不得不选择优化,这道题属于状态压缩类动态规划,在不久前小编就已经使用过了,其实说白了就是使用二进制优化。

  众所周知,二进制是这样的:1010110,一堆0和1,但是如果你的地图是由0、1组成的,那么就可以这样优化。

  图解一下:

  其实是从一本通提高篇上copy下来的。

  那么一行的数据就浓缩成一个数了,那么我们在处理1时就只要用lowbit原理x&-x就可以找到最高位的1了(别问我为什么,别的大佬都不太清楚)。

  好了,回归正题,这道题是动态规划题,那么怎样设计状态呢?

  我们要在n*n的棋盘上放k个国王,那么我们不妨用f[i][j][k]来表示第i行的状态是a[j](二进制表示),目前为止放了k个棋子。

  显然f[i][j][k]=sum{i-1,l,k-num[j]}(不发生冲突),其中l是,枚举上一行状态的变量,num[j]是a[j]这一状态可以放置的国王数。

  行了,其他的都看注释吧,要不然不好解释,代码如下:

 1 #include<iostream>
 2 using namespace std;
 3 long long n,K,num[1000],a[1000],sum,ans,f[300][300][300];
 4 void pre()
 5 {
 6     int cnt=0;//记录方案数 
 7     for(int i=0;i<(1<<n);i++)//注意,这里模拟的是一行的所有状态,而不是行数 
 8     {
 9         if(i&(i<<1)) continue;//如果结果不是0,那么就会有1是挨着的,不合法 
10         cnt=0;
11         for(int j=0;j<n;j++) if((1<<j)&i) cnt++;//如果i的二进制表示下的j位有1,那么就记录 
12         a[++sum]=i;//记录状态 
13         num[sum]=cnt;//记录可以放国王的数量 
14     }
15 }
16 void dp()
17 {
18     f[0][1][0]=1;//显然第0行第一位上可以放一个国王 
19     for(int i=1;i<=n;i++)
20     for(int j=1;j<=sum;j++)
21     for(int k=0;k<=K;k++)
22     {
23         if(k>=num[j])//先看能不能减 
24         for(int l=1;l<=sum;l++)
25         if(!(a[l]&a[j]) && !(a[l]&(a[j]<<1)) && !(a[l]&(a[j]>>1)))//看看国王和上一行的国王起不起冲突 
26         f[i][j][k]+=f[i-1][l][k-num[j]];
27     }
28     for(int i=1;i<=sum;i++) ans+=f[n][i][K];//最后一行的所有状态都要加上 
29     cout<<ans;
30 }
31 int main()
32 {
33     cin>>n>>K;
34     pre();dp();
35     return 0;
36 }
posted @ 2019-07-17 19:30  c1714-gzr  阅读(500)  评论(0编辑  收藏  举报