[状压动规] [luoguP1896] [SCOI2005] 互不侵犯King

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

输入输出格式

输入格式:

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式:

所得的方案数

输入输出样例

输入样例#1:
3 2
输出样例#1:
16

俺的题解

  也是一道状压的入门题目,大致结构与上道题一致。

  最大的区别是多加了一维判断“已使用的国王数”的状态,不能超过K。

  对于每一行的国王,考虑上一行与他不相邻的state,进行状态转移。

 

  状态转移方程:

  dp[当前行][枚举当前行的状态][当前行用的国王数+枚举的上一行用的国王数] += sigma(dp[上一行][枚举的上一行可行状态][枚举的上一行用的国王数])

 

俺的代码

 1 /*
 2 */
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cstring>
 6 using namespace std;
 7 int n, p, cnt = 0;
 8 int state[1<<10], sum[150];
 9 long long dp[15][1<<10][100];
10 
11 
12 int main()
13 {
14     memset(dp, 0, sizeof(dp));
15     memset(sum, 0, sizeof(sum));
16     scanf("%d%d", &n, &p);
17     for(int i = 0; i < (1 << n); i++)
18     {
19         if(! (i & i<<1))
20         {
21             state[++cnt] = i;
22             int temp = i;
23             while(temp)//计算到达此状态用了的国王数 
24             {
25                 sum[cnt] += (temp % 2);
26                 temp /= 2;
27             }
28         }
29     }
30     for(int i = 1; i <= cnt; i++)
31     {
32         if(sum[i] > p)
33             continue;
34         dp[1][state[i]][sum[i]]=1;
35     }
36     for(int i = 2; i <= n; i++)
37     {
38         for(int j = 1; j <= cnt; j++)
39         {
40             for(int k = 1; k <= cnt; k++)
41             {
42                 if(state[j] & state[k])
43                     continue;
44                 if(state[j] & (state[k] << 1))
45                     continue;
46                 if((state[j] << 1) & state[k])
47                     continue;
48                 for(int t = 1; t <= p; t++)
49                 {
50                     if(sum[j] + t > p)
51                         continue;
52                     dp[i][state[j]][sum[j] + t] += dp[i-1][state[k]][t]; 
53                 }
54             }
55         }
56     }
57     long long ans = 0;
58     for(int i = 1; i <= n; i++)
59     {
60         for(int j = 1; j <= cnt; j++)
61         {
62             ans += dp[i][state[j]][p];
63         }
64     }
65     printf("%lld\n", ans);
66     return 0;
67 }
点击就送屠龙宝刀

 

俺的反思

 有一些细节上的问题耽误了很多时间,必须反思。

  1、用乘法原理大概估计,会爆int。

  2、爆完int后改long long,对应的输入输出类型也要更改。

  3、因为对状态压缩没有熟练到一定程度,所以一个关键大括号写错了位置。需再熟练。

  4、位运算的左移代表了2的i次方,没有预估好空间,定义时爆掉了。

    在此记录,全局变量大约能开到4.9*108,当然这不代表比赛时可以开这么大。

  5、状压的那一位,数据范围一般只能在15以内,如果远远高于15,数组直接爆了,别说别的操作了……

    于是刚才有一道比较复杂的判重范围是500,激动地写了个状压的我真是不能再傻逼了。

posted @ 2017-09-16 10:34  秃猴  阅读(238)  评论(0编辑  收藏  举报