P1896(狀態壓縮)

題目

放棋子問題,放的是國際象棋的王,不能放的是棋子的周圍8格

題解

周圍8格相對好判斷的,先預處理,把所有可以一行存儲的給找出來,判斷方式是(i>>1)&i==0,並記憶存儲每一種方式的棋子數,根據棋子數進行排序,方便之後的剪枝。
接着就是判斷上下兩行之間的關係,就是將上一行的狀態,上一行前移一格,後移一格之後的三種狀態與這一行的狀態進行與操作只要結果不爲0,就不可以

代碼

#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
struct e
{
	int v,n;
}dp[1024];
int cnt,n,k;
ll qi[105][100][1024];
bool cmp(e a,e b)
{
	if(a.n==b.n) return a.v<b.v;
	else return a.n<b.n;
}
int suan(int i)
{
	int num=0;
	while(i){
		if(i&1) num++;
		i=(i>>1);
	}
	return num;
}
void init(int n)
{
	for(int i=0;i<=(1<<n)-1;i++){
		if(((i>>1)&i)==0){
			dp[++cnt].v=i;
			dp[cnt].n=suan(i);
		}
	}
	sort(dp+1,dp+cnt+1,cmp);
	/*for(int i=1;i<=cnt;i++){
		cout<<dp[i].n<<' '<<dp[i].v<<endl;
	}*/
}
int judge(int m,int n)
{
	if(n&m) return 0;
	if((n>>1)&m) return 0;
	if((n<<1)&m) return 0;
	return 1;
}
void ans(int m)
{
	for(int i=1;i<=cnt;i++){//遍歷上一層的狀態 
		for(int j=0;j<=k;j++){//遍歷之前所有層的棋子數 
			for(int l=1;l<=cnt;l++){//遍歷這一層的狀態 
				if((dp[l].n+j)>k) break;
				if(judge(dp[l].v,dp[i].v)) qi[m][j+dp[l].n][dp[l].v]+=qi[m-1][j][dp[i].v];
			}
		}
	}
}
int main()
{
	cin>>n>>k;
	init(n);
	for(int i=1;i<=cnt;i++){
		qi[1][dp[i].n][dp[i].v]=1;
	}
	for(int i=2;i<=n;i++){
		ans(i);
	}
	ll num=0;
	for(int i=1;i<=cnt;i++){
		num+=qi[n][k][dp[i].v];
	}
	cout<<num<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章