zoukankan      html  css  js  c++  java
  • [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;
    }
  • 相关阅读:
    最详解JavaScript常见的创建对象的七种方式(推荐)
    详解数组的迭代方法every()、filter()、forEach()、map()以及some()的用法
    详解数组的concat()、slice()、splice()方法
    解决微信小程序中web-view无法调用微信支付
    SQLAlchemy_定义(一对一/一对多/多对多)关系
    常用算法
    Python框架之Tornado (源码之褪去模板外衣)
    Python框架之Tornado(源码之褪去模板外衣的前戏)
    Python框架之Tornado(请求)
    Python框架之Tornado(请求阶段)
  • 原文地址:https://www.cnblogs.com/yjyl0098/p/14627221.html
Copyright © 2011-2022 走看看