互不侵犯KING

题意:一个$n * n$的棋盘,有$k$个国王,问有几种摆放的情况?

思路:爆搜直接寄,状压DP,将一行的状态表示为二进制状态比如101001,1表示这个位置放国王,然后转化成10进制数字,状压DP的套路就是不断枚举每个状态的数字,判断这种状态符不符合。

状态设置:

我们设置$ f[i][j][s]$ 表示我们选到第i行,第i行的状态为$S$ ,用了$j$个国王的方案数。

转移方程:

红色表示国王,蓝色是攻击范围,当我们第i-1行的第j列有国王时,第i行有什么影响呢?

  • 第i行的第j列,第j+1,j-1列都不能放了。我们用$S1$表示第i-1行的状态,$S2$表示i行的状态,那么就是当(S2&S1==0)&&((S2<<1)&S1)==0&&((S2>>1)&S1==0
  • 第$i$行来说,那么就需要满足((S2<<1)&S2==0)&&((S2>>1)&S2==0);

然后考虑第二维的国王数是怎么转移的,对第i-1行来说,第i行多放的国王数就是第i行上的1的个数,我们用__builtin_popcount这个函数快速找到,那么转移方程式即是:$f[i][j][S2]$+=$f[i-1][j-cnt[S2]][S1]$

初始化细节提示:

$f[0][0][0]=1$,考虑1到(1<<n)这些状态下,哪些状态是满足条件二的,然后每次DP,枚举这些状态即可。

代码:

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int f[10][100][1<<11];
int cnt[1<<11];
int st[1<<11];
//int king [1<<11],state[1<<11];
//int tot,ans;
//void  init(){
//    tot=(1<<n)-1;
//    for (int i = 0; i <=tot ; ++i) {
//        if(!((i<<1)&i)){
//            state[++ans]=i;
//            int t=i;
//            while (t){
//                king[ans]+=t%2,t>>=1;
//            }
//        }
//    }
//}
int num;
void init(){
    for (int s = 0; s <(1<<n) ; ++s) {
        cnt[s]=__builtin_popcount(s);
        if((((s<<1)|(s>>1))&s)==0) st[++num]=s;
    }
}
void solve(){
    cin>>n>>k;

    init();
    //当前的第i行,状态为S时,用了j个国王的方案数
    f[0][0][0]=1;
   for (int i = 1; i <=n ; ++i) {
       for (int tem = 1; tem <=num ; ++tem) {
           int s1=st[tem];
           for (int last = 1; last <=num ; ++last) {
               int s2=st[last];
               if(((s2|(s2<<1)|(s2>>1))&s1)==0){
                   for (int j = 0; j <=k ; ++j) {
                       if(j-cnt[s1]>=0){
                           f[i][j][s1]+=f[i-1][j-cnt[s1]][s2];
                       }
                   }
               }
           }
       }
   }
//     for(int i=1;i<=n;i++)         //枚举我们已经放到了第几行
//     {
//         for(int l=1;l<=num;l++)   //枚举第i行的状态,这里我们直接枚举所有满足条件2的状态,算是个优化吧
//         {
//             int s1=st[l];
//             for(int r=1;r<=num;r++) //枚举上一行的状态
//             {
//                 int s2=st[r];
//                 if(((s2|(s2<<1)|(s2>>1))&s1)==0)  //如果上下,左下右上,左上右下方向都不相邻,合法
//                 {
//                     for(int j=0;j<=k;j++) //枚举国王个数
//                         if(j-cnt[s1]>=0)
//                             f[i][j][s1]+=f[i-1][j-cnt[s1]][s2];  //状态转移方程
//                 }
//             }
//         }
//     }
    int ans=0;
//    cout<<num<<' ';
    for (int i = 1; i <=num ; ++i) {
        ans+=f[n][k][st[i]];
//        cout<<f[n][k][st[i]]<<' ';
    }
    cout<<ans<<'\n';

}

signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int t=1;
    //cin>>t;
    while (t--)solve();

}
posted on 2023-07-31 01:34  IR101  阅读(18)  评论(0编辑  收藏  举报  来源