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;
}
原文地址:https://www.cnblogs.com/yxr001002/p/14426363.html