小国王 状态压缩DP

【题目】
在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。
【输入格式】
共一行,包含两个整数 n 和 k。
1≤n≤10 ,0≤k≤n2
【输出格式】
共一行,表示方案总数,若不能够放置则输出0。
【输入样例】
3 2
【输出样例】
16

方法1:

#include<bits/stdc++.h> 
using namespace std; 
int n,k; //棋盘行数,国王总数 
int cnt; //同一行的合法状态个数 
int s[1<<12]; //同一行的合法状态集 
int num[1<<12]; //每个合法状态包含的国王数 
long long f[12][144][1<<12];
//f[i,j,a]表示前i行放了j个国王,第i行第a个状态时的方案数 

int main()
{
  cin>>n>>k;
  //找出一行的合法状态和每个合法状态包含的国王数 
	for(int i=0; i<(1<<n); i++){ //枚举一行的所有状态 
		if((i&i<<1)==0){ //如果不存在连续1 
			s[cnt++]=i; //保存一行的合法状态i 
			for(int j=0; j<n; j++)
				num[i]+=(i>>j&1);//统计每个合法状态包含的国王数 
		}
  }
  //从第i-1行向第i行状态转移 
	f[0][0][0]=1;	//不放国王也是一种方案 
	for(int i=1; i<=n+1; i++) //枚举行 
		for(int j=0; j<=k; j++) //枚举国王数 
  		for(int a=0; a<cnt; a++) //枚举第i行的合法状态 
	  		for(int b=0; b<cnt; b++) //枚举第i-1行的合法状态 
	  		{
	  			int c=num[s[a]]; //第i行第a个状态的国王数 
	  			//可以继续放国王,同列不攻击,斜对角不攻击 
	    		if((j>=c)&&!(s[b]&s[a])&&!(s[b]&(s[a]<<1))&&!(s[b]&(s[a]>>1)))	
	    	  	f[i][j][a]+=f[i-1][j-c][b]; //从第i-1行向第i行转移 
				}
	cout<<f[n+1][k][0]<<endl; //第n+1行什么都不放,相当于只在1~n行放国王 
	
  return 0;
}

方法2:

#include<bits/stdc++.h>
using namespace std;
long long f[12][144][1<<12];//f[i,j,k]前i行,j个国王,第i行状态为k时的方案数 
int t,s[1<<12];
int num[1<<12];
int p[1<<12],h[1<<12][1<<12];//
int n,m;

int main()
{
  cin>>n>>m;
  //预处理1:同行合法化
	for(int i=0;i<(1<<n);i++){
		if((i&i<<1)==0){
			s[t]=i; //合法状态 
			for(int j=0;j<n;j++)
				num[i]+=(i>>j&1); //合法状态的国王数
			//printf("s[%d]=%d c[%d]=%d\n",t,i,i,num[i]);
			t++;
		}
  }	
  //预处理2:相邻行合法化
  for(int i=0; i<t; i++){
    for(int j=0; j<t; j++){
      int a = s[i];
      int b = s[j];
      if(!(a&b)&&!(a&b>>1)&&!(a&b<<1)){ //相邻行状态a和b:同列不是1,邻列不是1 
        h[i][p[i]]=j;  //如果这两个状态可以满足条件即可以做邻居 
       	//printf("%d %d h[%d][%d]=%d\n",a,b,i,p[i],j); 
       	p[i]++;
			}
    }
  }

	f[0][0][0]=1;	
	for(int i=1;i<=n+1;i++)
		for(int j=0;j<=m;j++)
	  	for(int a=0;a<t;a++)
	  		for(int b=0;b<p[a];b++){
	  			int c=num[s[a]];
		    	if(j>=c){
		    	  	f[i][j][a]+=f[i-1][j-c][h[a][b]];
		    	  	//printf("%d %d: f[%d %d %d]=%d f[%d %d %d]=%d\n",a,b,i,j,a,f[i][j][a],i-1,j-c,h[a][b],f[i-1][j-c][h[a][b]]);		  				
					}

				}
	cout<<f[n+1][m][0]<<endl;
  return 0;
}

方法3:

#include<bits/stdc++.h>
using namespace std;
const int N = 12;
int n,k; //棋盘行数,国王总数 
vector<int> s; //同一行的合法状态集 
vector<int> h[1<<N]; //每个状态可以转移到的其他状态的集合 
int num[1<<N]; //每个合法状态包含的国王数
long long f[N][N*N][1<<N];
//f[i,j,a]表示前i行放了j个国王,第i行第a个状态时的方案数

int main()
{
  cin>>n>>k;
  //预处理阶段1:同一行合法化
  for(int i=0; i<(1<<n); i++){ //枚举一行的所有状态 
    if(!(i&i>>1)){ //如果不存在连续1 
      s.push_back(i); //i合法
			for(int j=0;j<n;j++)
				num[i]+=(i>>j&1); //合法状态的国王数 
      printf("%d num[%d]=%d\n",i,i,num[i]);
    }
  }
  //预处理阶段2:相邻行合法化 
  for(int i=0; i<s.size(); i++){
    for(int j=0; j<s.size(); j++){
      int a = s[i], b = s[j];
      if(!(a&b) && !(a&b>>1) && !(a&b<<1)){ //相邻行状态a和b:同列不攻击,斜对角不攻击 
        h[i].push_back(j);  //如果这两个状态可以满足条件即可以做邻居 
       	printf("%d %d h[%d]=%d\n",a,b,i,j); 
			}
    }
  }
	//状态转移:从第i-1行向第i行转移 
  f[0][0][0]=1;	//不放国王也是一种方案
  for(int i=1; i<=n+1; i++) //枚举行
    for(int j=0; j<=k; j++) //枚举国王数
      for(int a=0; a<s.size(); a++) //枚举第i行的合法状态
        for(int b=0; b<h[a].size(); b++){ //枚举所有相邻行合法状态,只有相邻行合法才能从前一行转移过来
          int c=num[s[a]]; //第i行的所有行合法状态国王数
          if(j>=c) //可以继续放国王 
            f[i][j][a]+=f[i-1][j-c][h[a][b]]; //从第i-1行向第i行转移 
        }
  cout<<f[n+1][k][0]<<endl; //第n+1行什么都不放,相当于只在1~n行放国王
  
  return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值