• 状压DP Sgu223 骑士


    本人水平有限,题解不到为处,请多多谅解

    本蒟蒻谢谢大家观看

    题目:

    Problem K: Sgu223 骑士

    Time Limit: 1 Sec  Memory Limit: 128 MB
    Submit: 46  Solved: 25
    [Submit][Status][Web Board]

    Description

    在一个N*N的棋盘上放K个KING,KING可攻击相邻的8个位置。
    问有多少种方式让他们互不攻击。
    N<=10,0<=k<=N*N

    Input

    第一行给出数字N,K

    Output

    如题

    Sample Input

    3 2

    Sample Output

    16

    HINT

    f[i][j][k]表示以第i行的第j个状态上放k个国王的每行的总方案数

    f[11][145][101]是如何推出来的呢?
    首先 第一维 是表示行数;由题意得:n<=10;所以行数只需到11;
    其次 第二维 是表示每行的状态——例如:1 0 0 0 1 0 我们可以发现,任意一位是看
    它前面那个数;
    分情况:若前一位为0,则当前位可以任取,前两位可以任取;
        若前一位为1,则当前位只能为0,前两位只能为0;
    所以可得递推公式:f[i]=f[i-1]+f[i-2];(f[1]=2,f[2]=3)只有1位时,取何不取
    两种情况;当有两位时,1 0 ,0 1,0 0;三种情况。
    f[10]=144;我们第二维取145;
    最后 第三维 是表示国王数;n<=10;n*n<=100;就算全部放上国王也就100;
    所以,我们第三维取101;

    code:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll f[11][145][101],ans;
    //f[i][j][k]表示以第i行的第j个状态上放k个国王的每行的总方案数
    //1 0 0 0 1 →j 
    //_ _ _ _ _ →i 
    ll num[155], s[155], n, kk, k, sum;
    void find() {
        sum = 0, ans = 0;
        memset(f, 0, sizeof(f));
        for (ll i = 0; i < (1 << n); i++)
         //此时i枚举状态,j枚举列数(把每行分成n块,则每块就是列数) 
        {
            if (i & (i << 1)) //如果前后位置不能同时为1(同时放国王)
            //预处理:即先处理完行(前后)
            //那么进行DP转移时就不用再判断了 
                continue;
            kk = 0;
            for (ll j = 0; j < n; j++) 
            {
                if (i & (1 << j))
                    kk++; 
            //统计在状态为i的时候,对应位置有多少个1. 例如1001,有2个1 
            }
            s[++sum] = i;//相当与映射——把每一种状态转换为可以随时调用的数组 
            num[sum] = kk;//把每行所拥有的 1(国王数)记录下来 
        }
    }
    void dp() {
        f[0][1][0] = 1;
        //第0行可以放国王,但国王数为0,此时方案数为1 
        for (ll i = 1; i <= n; i++) //枚举行 
        {
            for (ll j = 1; j <= sum; j++) //枚举当前这一行的所有状态数() 
            {
                for (ll t = 1; t <= sum; t++)
                //枚举上一行所有状态数 
                {
                    for (ll kk = num[j]; kk <= k; kk++) 
                     //枚举前i行一共可以放多少个国王
                     //kk从num[j]开始,保证(kk-num[j])为正 
                    {
    if (!(s[t] & s[j]) && !(s[t] & (s[j] << 1)) && !(s[t] & (s[j] >> 1)))
    //对于状态s[t]与s[j],上下不能同时为1(同时放国王)
    //并且对角不能同时为1(同时放国王) 
    //每行我们不需要再进行判断,因为之前的预处理已经判断了 
                            f[i][j][kk] += f[i - 1][t][kk - num[j]];
                  //上一行的上一个状态(当前所有的总国王数-当前这一行总国王数)累加 
                    }
                }
            }
        }
        for (ll i = 1; i <= sum; i++) ans += f[n][i][k];
        //把以1~n为行数的所有状态总国王数累加 
        printf("%lld", ans);
    }
    int main() {
        scanf("%lld%lld", &n,&k);
        find();
        dp();
        return 0;
    }
  • 相关阅读:
    更新自己知识的网站
    CRM IFD 部署在同一台服务器上遇到的错误
    Dynamics CRM 修改Excel 最大导出记录限制及 最大上传文件限制
    CRM 插件奇怪的报错
    单实例,当MongoDB单表数据文件太大导致写入速度变慢
    MongoDB分片实战
    SpringCloud使用feign时的复杂参数传递(转)
    99%的人都理解错了HTTP中GET与POST的区别(转)
    MySQL 数据导入 Unknown MySQL server host 'localhost'
    会员积分体系设计思路(转)
  • 原文地址:https://www.cnblogs.com/nlyzl/p/11309637.html
Copyright © 2020-2023  润新知