用简短代码做出这个题,有这样一个题目:它的标签是深度搜索dfs,但是我做这个题目的时候却不知道从哪里下手,但是理解题目意思之后,这个代码解决就很轻松了。
题目是这样的:
【题目描述】
一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。
【输入】
一行一个正整数 n,表示棋盘是 n×n 大小的。
【输出】
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
【提示】
数据范围:
对于 100% 的数据,6≤n≤13。
样例输入
6
样例输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
6×6 的跳跳棋盘满足题意的四种情况:
解题思路
我首先想的是:把整个棋盘都遍历一遍,如果一个棋子被放置在一个点,那就把这个点的行,列及其两个斜线都用book二维数组标记为 1,之后要放置棋子要满足 book 数组的值为 0,然后放置后继续标记。
但是这个方法不太好实现,因为每选择一个棋子,就要把它的行,列及其斜线上面的book数组标记,并且实现了 n 列后,要取消标记。
由上述图可知:每一个皇后(棋子)都单独占一行,单独占一列,单独占从左上至右下的对角线,单独占从右上至左上的对角线。
那可以换一个角度,不是非要一个二维数组来标记,三个方向(列,两个斜线方向)都可以用一个数组标记(b[100],c[100],d[100] 初始化为 0 ),并且每一个数组被标记了,那个所标记的点就不能放置棋子了(如b[1]=1,代表第一列被标记了,不能放棋子了)。那行 i 用不用标记呢?行是需要标记的,但是我们用行来循环,记录棋子的位置,记录行坐标的是 a[100],a[1]的值代表第一行棋子的位置。
现在又出现了一个问题,两个斜线方向要怎么用一维数组记录,然后标记呢?
可以找找规律......
每一条由右上至左下的斜线,行 i 加上列 j 是相等的;每一条由左上至右下的斜线,行 i 减去列 j 是相等的,但是这里要注意一个问题:有些数据的行 i 减去列 j 是负数,而对于这个数字,数组会越界而不能被标记。
然后我第一个想法是:用行减去列的绝对值 abs( i - j )来记录左上至右下的斜线,但是这样计算出来的会有不同的斜线重复,数组不能准确代表某一个斜线,所以用行减去列再加上棋盘的范围 i - j + n 来记录,这个( i - j + n)也始终是正数。
这些问题都解决了,就可以用代码解决了:
代码如下:
#include<stdio.h>
int a[100],b[100],c[100],d[100];
int n,s;
void fun(int k)//k为行数
{
int i,j;
for(i=1;i<=n;i++)//遍历列
{
if(b[i]==0&&c[i+k]==0&&d[i-k+n]==0)
{
//如果满足未被三个数组标记的条件,就用数组 a 记录下这个第 k 行的列数
//并且把这个放置棋子的点标记
a[k]=i;
b[i]=1;
c[i+k]=1;
d[i-k+n]=1;
if(k==n)
{
//题目要求输出前三个方案,每一行代表的数组
s++;
//那就用一个 if 语句判断
if(s<=3)
{
for(j=1;j<=n;j++)
printf("%d ",a[j]);
printf("\n");
}
}
//如果每行没有遍历到,则继续向下进入函数
else
fun(k+1);
//出函数要把标记解除,否则会影响下一个循环
b[i]=0;
c[i+k]=0;
d[i-k+n]=0;
}
}
}
int main()
{
scanf("%d",&n);
fun(1);//从行数为 1 开始遍历
printf("%d",s);//输出方案的个数
return 0;
}
这个题目大概就是这样,注意标记可以用多个数组共同实现。