[SCOI2005]互不侵犯 题解

题目链接

 

题目


 

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

输入格式

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

输出格式

所得的方案数

输入样例

3 2

输出样例

16
 
 

题解


 

 

这是一道很经典的状态压缩dp的题目,基于连通性;

首先开始想状态转移方程,我们可以按行来枚举

f [ i , j , s] 表示已经摆好了前 i 行,并且第 i 行 的状态为 s,已经摆了 j 个国王的方案数

可以先预处理出来所有合法的方案,

 

  • 1.对于同一行来说 ,要满足不存在两个连续的1(不然就会被攻击)
  • 2.同时上下两行状态的与 必须等于0(没有并列的)同时上下两行或的状态也得是满足同一行没有两个连续的1才行

我们发现经过这样处理后第i - 1 行的状态与第 i 行的状态是成映射关系

这时候我们直接预处理每一行的前继状态有哪些,在方程转移时直接转移就可以了

最后算一下复杂度 总状态是i * j * s 也就是10 * 100 *  210 大概是106;

每次转移最多每个状态都转移也就是 210,总计应该是 109

看似过不了,但其实我们枚举的状态有很多冗余状态,这里我们在预处理的时候已经判掉了,

经过后期程序测试发现, S状态 * 状态转移的大小基本上不会超过1365;

这样一算复杂度就只有一百万了,可以过,状压dp的很多题都是这样——至少得先写写试试吧

 

程序


 

 

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 12,M = 1 << 10;
const int K = 110;
long long f[N][K][M];

vector<int> state;
vector<int> head[M];
int cnt[M];
int n,k;


bool check(int x)
{
    for(int i = 0; i < n; ++ i)
    {
        if((x >> i & 1) && (x >> i + 1) & 1) return false;
    }
    return true;
}

int count(int x)
{
    int res = 0;
    for(int i = 0; i < n; ++ i) res += x >> i & 1;
    return res;
}




int main()
{
    cin >> n >> k;
    for(int i = 0; i < 1 << n; ++ i)
       if(check(i))
       {
           cnt[i] = count(i);
           state.push_back(i);
       }
    for(int i = 0; i < state.size(); ++ i)
       for(int j = 0; j < state.size(); ++ j)
       {
           int a = state[i],b = state[j];
           if((a & b) == 0 && check(a | b)) 
           {
               head[i].push_back(j);
           }
       }
    f[0][0][0] = 1;
    for(int i = 1;i <= n + 1; ++ i)
      for(int j = 0; j <= k; ++ j)
          for(int a = 0; a < state.size(); ++ a)
            for(int b = 0; b < head[a].size(); ++ b)
            {
                int u = head[a][b];
                int c = cnt[state[a]];
                if(j >= c) f[i][j][a] += f[i - 1][j - c][u];
            }
    cout << f[n + 1][k][0];
    return 0;
}

 

 

 

 

 

 

posted @ 2021-04-07 12:57  Linyk  阅读(153)  评论(0编辑  收藏  举报