zoukankan      html  css  js  c++  java
  • 1592:【例 1】国王

    1592:【例 1】国王


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

    【题目描述】

    原题来自:SGU 223

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

    【输入】

    只有一行,包含两个整数 nn 和 kk。

    【输出】

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

    【输入样例】

    3 2

    【输出样例】

    16

    【提示】

    样例输入 2

    4 4

    样例输出 2

    79

    数据范围与提示:

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

    思路:

    首先考虑搜索,发现可剪枝状态少;接着二维dp,发现状态难以表达

    发现国王只影响周围八个格子,也就是说国王只影响这一行的状态和上下两行的状态,可以压缩整行信息,进行状压

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

    考虑如何表示a[](二进制,0表示不放,1表示放)

    算法流程:

    1.对于每一行,搜索得出一个合法状态

    2.枚举上一行与这一行相容的状态累加

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    long long f[11][155][155]={0},ans;
    int num[155],s[155],n,k,s0;
    
    void prepare()
    {
        int cnt;
        s0=0,ans=0;
    //    memset(f,0,sizeof(f));
        for(int i=0;i<(1<<n);i++)//枚举每一行的状态,总方案数为2^n
        {
            if(i&(i<<1))//检查当前状态是否冲突(当前状态是否存在两个相邻的国王)
            continue;
            cnt=0;
            for(int j=0;j<n;j++)
            if(i&(1<<j))cnt++;//k表示当前状态用掉的国王数 
            s[++s0]=i;//最终s数组中存放的是当前行的所有可用状态 
            num[s0]=cnt;
         } 
    }
    
    void dp()
    {
        f[0][1][0]=1;
        for(int i=1;i<=n;i++)//
            for(int j=1;j<=s0;j++)//状态 
                for(int kk=0;kk<=k;kk++)//国王数量
                {
                    if(kk>=num[j])//若是当前拥有的国王数比这一行需要的国王数多
                    for(int t=1;t<=s0;t++)//枚举第i-1行的状态
                    {
                        if(!(s[t]&s[j])&&!(s[t]&(s[j]<<1))&&!(s[t]&(s[j]>>1)))//没有冲突
                        f[i][j][kk]+=f[i-1][t][kk-num[j]]; 
                     } 
                 } 
        
        for(int i=1;i<=s0;i++) ans+=f[n][i][k];
        cout<<ans<<'
    ';
        /*for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=s0;j++)
            {
                for(int kk=0;kk<=k;kk++)
                    cout<<f[i][j][kk]<<" ";
                    cout<<'
    ';
            }
            cout<<'
    ';
        }
        
        cout<<s0<<"@@@@@
    ";
        cout<<f[3][3][4]<<'
    ';
        */    
                
        /*f[0][1][0]=1;//显然第0行第一位上可以放一个国王
        for(int i=1;i<=n;i++)
        for(int j=1;j<=s0;j++)
        for(int kk=0;kk<=k;kk++)
        {
            if(kk>=num[j])//先看能不能减
            for(int t=1;t<=s0;t++)
            if(!(s[t]&s[j]) && !(s[t]&(s[j]<<1)) && !(s[t]&(s[j]>>1)))//看看国王和上一行的国王起不起冲突
            f[i][j][kk]+=f[i-1][t][kk-num[j]];
        }
        for(int i=1;i<=s0;i++) ans+=f[n][i][k];//最后一行的所有状态都要加上
        cout<<ans;*/
    }
    
    int main()
    {
        cin>>n>>k;
        prepare();
        dp();//使用函数更方便 
        return 0;
    }
  • 相关阅读:
    Java 简单算法--打印乘法口诀(只使用一次循环)
    Java简单算法--求100以内素数
    ubuntu 16.04 chrome flash player 过期
    java 网络API访问 web 站点
    java scoket (UDP通信模型)简易聊天室
    leetcode1105 Filling Bookcase Shelves
    leetcode1140 Stone Game II
    leetcode1186 Maximum Subarray Sum with One Deletion
    leetcode31 Next Permutation
    leetcode834 Sum of Distances in Tree
  • 原文地址:https://www.cnblogs.com/yxr001002/p/14426363.html
Copyright © 2011-2022 走看看