AcWing 1064. 小国王 (状态压缩DP)

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

输入格式

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

输出格式

共一行,表示方案总数,若不能够放置则输出00。

数据范围

1≤n≤101≤n≤10,
0≤k≤n20≤k≤n2

输入样例:

3 2

输出样例:

16

 思路:

状态压缩DP

f(i,j,m)表示第i行已经使用了j个国王,状态为m时的方案数  ;

上下两行的状态  a ,b  如果不冲突需要满足的关系为   

  • a、b本身时合法的(二进制没有连续的1出现)
  • a|b 的结果是合法的
  • a&&b==0

 所以,预处理判断 [1,1<<n] 所有数字二进制的状态,储存合法的状态,并且预处理和储存所有相邻两行的合法状态;之后开始DP

  • 转移状态方程 : f( i , j, a) += f( i-1, j-c, b)     c 是当第i行为状态a时能放的国王数量

答案就是 max{  f( n, k, a)  }  枚举所有合法状态a

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12,M=1<<N,K=110;
vector<int> state;//储存合法状态
int cnt[M];
vector<int> h[M];//记录所有相邻得合法状态
ll f[N][K][M];
int n,k;
bool check(int x)//判断是否有相邻的1(两个国王挨着放)
{
    if(x&(x<<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)) state.push_back(i),cnt[i]=count(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)) h[a].push_back(b);
        }
    }
    f[0][0][0]=1;//初始化
    for(int i=1;i<=n+1;i++)
    {
        for(int j=0;j<=k;j++)
        {
            for(int x=0;x<state.size();x++)
            {
                for(auto y : h[state[x]])
                {
                    int c=cnt[state[x]];
                    if(j>=c)//当前行的的这个状态中国王的数量要小于此刻国王总数
                    {
                        f[i][j][state[x]]+=f[i-1][j-c][y];
                    }
                }
            }
        }
    }
    cout <<f[n+1][k][0]<<endl;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值