Acwing--3494. 国际象棋(状态压缩dp)

众所周知,“八皇后” 问题是求解在国际象棋棋盘上摆放 88 个皇后,使得两两之间互不攻击的方案数。

已经学习了很多算法的小蓝觉得 “八皇后” 问题太简单了,意犹未尽。作为一个国际象棋迷,他想研究在 N×MN×M 的棋盘上,摆放 KK 个马,使得两两之间互不攻击有多少种摆放方案。

由于方案数可能很大,只需计算答案除以 10000000071000000007 (即 109+7109+7) 的余数。

如下图所示,国际象棋中的马摆放在棋盘的方格内,走 “日” 字,位于 (x,y)(x,y) 格的马(第 xx 行第 yy 列)可以攻击 (x+1,y+2)(x+1,y+2)、(x+1,y−2)(x+1,y−2)、(x−1,y+2)(x−1,y+2)、(x−1,y−2)(x−1,y−2)、(x+2,y+1)(x+2,y+1)、(x+2,y−1)(x+2,y−1)、(x−2,y+1)(x−2,y+1) 和 (x−2,y−1)(x−2,y−1) 共 88 个格子。

QQ截图20210512104039.png

输入格式

输入一行包含三个正整数 N,M,KN,M,K,分别表示棋盘的行数、列数和马的个数。

输出格式

输出一个整数,表示摆放的方案数除以 10000000071000000007 (即 109+7109+7) 的余数。

数据范围

对于 5%5% 的评测用例,K=1K=1;
对于另外 10%10% 的评测用例,K=2K=2;
对于另外 10%10% 的评测用例,N=1N=1;
对于另外 20%20% 的评测用例,N,M≤6,K≤5N,M≤6,K≤5;
对于另外 25%25% 的评测用例,N≤3,M≤20,K≤12N≤3,M≤20,K≤12;
对于所有评测用例,1≤N≤6,1≤M≤100,1≤K≤201≤N≤6,1≤M≤100,1≤K≤20。

输入样例1:

1 2 1

输出样例1:

2

输入样例2:

4 4 3

输出样例2:

276

输入样例3:

3 20 12

输出样例3:

914051446

 

 求lowbit的函数来自Acwing--801. 二进制中1的个数(位运算)_with_wine的博客-CSDN博客

这个题类似于 Acwing  292. 炮兵阵地

思路可以看代码里的注释🤗🤗

#include<bits/stdc++.h>
using namespace std;
const int N=1<<6,M=110,T=30,mod=1000000007;


int n,m,k;

long long f[M][N][N][T];//f[i][a][b][t]前i列,第i-1列的状态为a,第i列的状态为b,此时用的马总数为t条件下的方案数


int lowbit(int x)//计算x中有多少个1,带入题目中就是增加了多少个马
{
    int ans=0;
    while(x)
    {
        x-=x&(-x);
        ans++;
    }
    return ans;
    
}



int main()
{
    cin>>n>>m>>k;
    
    f[0][0][0][0]=1;//初始化方案数
    
    int maxn=1<<n;
    
    for(int i=1;i<=m;i++)
       for(int a=0;a<maxn;a++)//i-2
          for(int b=0;b<maxn;b++)//i-1
          {
              if((a>>2)&b||a&(b>>2))continue;//a,b有冲突的情况(剪枝)
			   for(int c=0;c<maxn;c++)
               {
                  if((c>>1)&a||c&(a>>1))continue;//c和b冲突
                  if((c>>2)&b||c&(b>>2))continue;//c和a冲突
                     int t=lowbit(c);
                     for(int j=t;j<=k;j++)
                       f[i][b][c][j]=(f[i][b][c][j]+f[i-1][a][b][j-t])%mod;
                     
                      
               }
          }
            
                  
                        
    
    int res=0;
    for(int i=0;i<maxn;i++)
       for(int j=0;j<maxn;j++)
          res=(res+f[m][i][j][k])%mod;
          
    
    cout<<res<<endl;
    
    
    
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值