状态压缩.互不侵犯king

bzoj1087

题目描述

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

输入格式

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式

一行,一个整数,表示答案。

样例数据

input

3 2

output

16

数据规模与约定

N<=10
这道题也是比较典型的状态压缩题,以f[i][j][k]表示在第k行的第i个状态下,剩余i个棋子要放的方案数。那么我们预处理出所有的合法状况,存到一个数组中,再开个布尔数组check[i][j]表示i和j能否作为相邻的两行。然后只要递推就行了,把下一行符合情况的f[][][]全部累加到当前的f中,唯一要注意的是f中的第一个变量代表的是状态的编号还是状态要确定,假如打的时候打错了可能就要陷入调试了……
AC代码如下:(代码可能有点冗长,大佬轻喷0.0)

#include<bits/stdc++.h> 
using namespace std; 
long long f[100][90][10];//f[i][j][k]表示 第k行,i状态,还剩余j个棋子的方案数 
bool    ff[100][90][10];
int n,m,now[100],q,sum[100];//now数组存的是每一个合法状态,q是数量 ,sum存的是状态对应含有1数量 
bool hy[20],check[100][100];//check[i][j]表示两个状态能否相邻 
int mi(int num,int sum){if(num==0) return sum;sum*=2;num--;return mi(num,sum);}//2^n 
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
} 
void first() 
{ 
 for(int i=0;i<=mi(n,1)-1;++i) 
  { 
     memset(hy,0,sizeof(hy)); 
     int j=1,num=i; 
     bool flag=false; 
     for(int k=mi(n-1,1);k>=1;k/=2,++j) 
      { 
       if(k<=num&&hy[j-1]) {flag=true;continue;} 
       if(k<=num) num-=k,hy[j]=true; 
      } 
     if(flag) continue;
     q++; 
     for(int k=1;k<=n;++k)                     
      if(hy[k]) now[q]+=mi(n-k,1);                 
  } 
 memset(hy,0,sizeof(hy));
 for(int i=1;i<=q;++i)
  for(int j=i+1;j<=q;++j)
   {
    int num=now[i]|now[j];
    bool chec=true;
    if((now[i]&now[j])!=0) continue;
    while(num>0)
     {
        bool flag=true;
        if(num%2==1) flag=false;
        num/=2;
        if(num%2==1&&flag==false) {chec=false;break;} 
     }
    if(chec) check[i][j]=check[j][i]=true; 
   }                      
 check[1][1]=true;
 for(int i=1;i<=q;++i)
  {
    int num=now[i];
    while(num>0)
     {
        if(num%2==1) sum[i]++;
        num/=2;
     }
  }
 return; 
} 
void dp(int zh,int rest,int hang)
{
 ff[zh][rest][hang]=true;
 if(rest==0) return;
 for(int i=1;i<=q;++i)
  {    
    if(!check[zh][i]) continue;
    if(rest-sum[i]<0) continue;
    if(f[i][rest-sum[i]][hang+1]==-1) continue;
    if(f[i][rest-sum[i]][hang+1]==0&&!ff[i][rest-sum[i]][hang+1]) {dp(i,rest-sum[i],hang+1),f[zh][rest][hang]+=f[i][rest-sum[i]][hang+1];} 
     else {f[zh][rest][hang]+=f[i][rest-sum[i]][hang+1];} 
  }
}
void work() 
{ 
 memset(f,0,sizeof(f));
 for(int i=1;i<=q;++i) for(int j=1;j<=n;++j) f[i][0][j]=1,f[i][0][j]=true;
 for(int i=1;i<=q;++i) for(int j=1;j<=m;++j) f[i][j][n]=-1;
 long long ans=0;
 for(int i=1;i<=q;++i) dp(i,m-sum[i],1),ans+=f[i][m-sum[i]][1];
 printf("%lld",ans);
 return; 
} 
int main() 
{ 
 n=read();m=read(); 
 first(); 
 work(); 
 return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值