飞行棋

做题时间:2021.02.03

\(【题目描述】\)

在一个\(N \times M(N,M \leq 10)\)的棋盘上,放置\(k(k \leq 10)\)种不同颜色的棋子将棋盘填满,每一种有无限个,在任意一条从左上角到右下角的路径上不得出现任意两个相同颜色的棋子。问有多少种方案数。

\(【输入样例】\)

样例一
2 2 4
0 0
0 0
样例二
3 3 5
0 0 0
0 0 0
4 0 0
样例三
3 3 5
0 0 0
0 0 1
0 0 0

\(【输出样例】\)

样例一
48
样例二
24
样例三
24

\(【考点】\)

搜索、剪枝

\(【做法】\)

\(【代码】\)

#include<cstdio>
#include<iomanip>
using namespace std;
const int MOD=1e9+7; 
int cnt[20];//记录当前第i个颜色出现的次数 
int f[20][20];//记录当前填色的情况 
int a[20][20];//记录棋盘情况 
int check(int x)//查找x中有多少个1,即之前已经填了多少个颜色 
{
	int ans=0;
	while(x){
		if(x&1) ans++;
		x>>=1;
	}
	return ans;
}
int n,m,k;
int DFS(int x,int y)
{
	if(y==m+1) x++,y=1;
	if(x==n+1) return 1;
	int now=f[x-1][y]|f[x][y-1];//剪枝2:获取当前所用的颜色总数 
	int p=-1,res=0;
	if(k-check(now)<n+m-x-y+1) return 0;//剪枝2:判断剩下的颜色是否大于之后的路径长度 
	for(int i=1;i<=k;i++){
		if((now>>i-1)&1) continue;//剪枝1:如果在当前第i个颜色已经填过,则不合法 
		if(a[x][y]==i||a[x][y]==0){ 
			cnt[i]++;
			f[x][y]=now+(1<<i-1);//将第i个颜色标记上 
			if(cnt[i]!=1){
				res+=DFS(x,y+1);
				res%=MOD;
			}
			else{//剪枝3:所有第一次出现的颜色,方案数都是一样的 
				if(p==-1){
					p=DFS(x,y+1);
					res+=p;
					res%=MOD;
				}
				else{
					res+=p;
					res%=MOD;
				}
			}
			cnt[i]--;
		}
	}
	return res;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
			if(a[i][j]) cnt[a[i][j]]++;//提前标记 
		}
	}
	printf("%d\n",DFS(1,1)%MOD);
	return 0;
}
posted @ 2021-02-04 09:44  lxzy  阅读(129)  评论(0)    收藏  举报