问题描述
八皇后问题是在棋盘上放皇后,互相不攻击,求方案。变换一下棋子,还可以有八车问题,八马问题,八兵问题,八王问题,注意别念反。在这道题里,棋子换成车,同时棋盘也得换,确切说,是进行一些改造。比如现在有一张n*n的棋盘,我们在一些格子上抠几个洞,这些洞自然不能放棋子了,会漏下去的。另外,一个车本来能攻击和它的同行同列。现在,你想想,在攻击的过程中如果踩到一个洞,便会自取灭亡。故,车的攻击范围止于洞。
此题,给你棋盘的规模n,以及挖洞情况,求放k个车的方案数(k从0到最多可放车数)
输入格式
第一行一个整数n表示棋盘大小
接下来n行,每行n个用空格隔开的数字0或1,0的形状表示洞,1表示没有洞
输出格式
若干行,第i行表示放i个车的方案数
样例输入
3
1 0 1
1 1 1
1 0 1
样例输出
7
12
4
数据规模和约定
n<=8
#include<iostream>
using namespace std;
int n;
int arr[10][10];//棋盘状态
int res[100]; //目标,统计从放1个车和n*n-1个车的种类数
bool check(int x,int y){
for(int i=x-1;i>=0;i--)//判断从(x-1,y) ~(0,y) 坐标位置 逆向遍历
if(arr[i][y]==2)//如果先遇到的是2(坐标上的数字为2表示此处已经放置了车),说明两个2之间没有 洞,那么这个位置是不能再放 车
return false;
else if(arr[i][y]==0)//如果先遇到了洞,就说明从(x-1,y) ~(0,y) 坐标位置,在洞之后是可以放置车的,所以该范围没有问题,于是跳出循环,判断下一个范围
break;
for(int i=x+1;i<n;i++)//判断从(x+1,y) ~(n-1,y) 坐标位置 逆向遍历
if(arr[i][y]==2)
return false;
else if(arr[i][y]==0)
break;
for(int i=y-1;i>=0;i--)/判断从(x,y-1) ~(x,0) 坐标位置 逆向遍历
if(arr[x][i]==2)
return false;
else if(arr[x][i]==0)
break;
for(int i=y+1;i<n;i++)/判断从(x,y+1) ~(x,n-1) 坐标位置 逆向遍历
if(arr[x][i]==2)
return false;
else if(arr[x][i]==0)
break;
return true;
}
//now用来遍历棋盘,sum是统计放置的车的数量
void dfs(int now,int sum){
if(now>=n*n){//当一次遍历结束时
res[sum]++;
return;
}
int x=now/n;//此处与输入棋盘状态一样,巧妙解决了两层循环遍历棋盘的问题
int y=now%n;
if(arr[x][y]==1)//当(x,y)位置为1,即能放车时
if(check(x,y)){//判断该位置是否符合条件
arr[x][y]=2;//标记该位置
dfs(now+1,sum+1);//遍历下个位置,车的数量加1
arr[x][y]=1;//回溯
}
dfs(now+1,sum);//如果该位置无法放车,那么遍历下个位置,车的数量不变
}
int main(){
cin>>n;
int nn=0;
//输入棋盘状态,只用一层循环,此种方法很巧妙
//n=3时,nn=0~8 i=0~2,j=0~2
while(nn<n*n){
int i=nn/n;
int j=nn%n;
cin>>arr[i][j];
nn++;
}
dfs(0,0);
for(int i=1;i<n*n;i++){
if(res[i]!=0)
cout<<res[i]<<endl;
else
break;
}
return 0;
}