题解 P1219 【八皇后】

写在前面:

第一次认真写题解QwQ求赞

我初学dfs时遇到的第一题应该就是这道吧

本文面向初学者,说的较为详细

侧重于作者的做题过程

我相信很多人都会像我一样去做

枚举每一行的皇后的位置即可

对角线的处理非常令人迷茫


第一次尝试:

一开始,我兴奋地打了这样一个代码:

用二维数组vis标记一下能不能放不就好了吗?

看题解说的花里胡哨的……

void dfs(int x){
	if(x==n+1){
		cnt++;
		if(cnt<=3){
			for(int i=1;i<=n;i++)
				printf("%d ",ans[i]);
			printf("\n");
		}
		return;
	}
	for(int i=1;i<=n;i++){
		if(!vis[x][i]){
			vis[x][i]=1;
			ans[x]=i;
			for(int j=x+1;j<=n;j++)
				vis[j][i]=1;
			for(int j=1;j<=n;j++){
				if(x+j>n||i+j>n) break;
				vis[x+j][i+j]=1;
			}
			for(int j=1;j<=n;j++){
				if(x+j>n||i-j<1) break;
				vis[x+j][i-j]=1;
			}
			dfs(x+1);
			for(int j=x+1;j<=n;j++)
				vis[j][i]=0;
			for(int j=1;j<=n;j++){
				if(x+j>n||i+j>n) break;
				vis[x+j][i+j]=0;
			}
			for(int j=1;j<=n;j++){
				if(x+j>n||i-j<1) break;
				vis[x+j][i-j]=0;
			}
		}
	}
	return;
}

内心无比欢悦!

——然鹅,样例都没过

原来会有重叠!回溯的时候改的并不是原来的值,原来已经否认了这种情况。


第二次尝试:

瞄了眼看起来极小的数据范围,懒人我怎么会不偷懒呢。

直接拿个三维数组viss存一下不就好了,更新还方便,无脑。

一心以为复杂度炸不了的
(我居然算都没算……)

于是这样一份代码横空出世……

void dfs(int x){
	if(x==n+1){
		cnt++;
		if(cnt<=3){
			for(int i=1;i<=n;i++)
				printf("%d ",ans[i]);
			printf("\n");
		}
		return;
	}
	for(int i=1;i<=n;i++){
		if(!vis[x][i]){
			for(int j=1;j<=n;j++)
				for(int k=1;k<=n;k++)
					viss[x][j][k]=vis[j][k];
			vis[x][i]=1;
			ans[x]=i;
			for(int j=x+1;j<=n;j++)
				vis[j][i]=1;
			for(int j=1;j<=n;j++){
				if(x+j>n||i+j>n) break;
				vis[x+j][i+j]=1;
			}
			for(int j=1;j<=n;j++){
				if(x+j>n||i-j<1) break;
				vis[x+j][i-j]=1;
			}
			dfs(x+1);
			for(int j=1;j<=n;j++)
				for(int k=1;k<=n;k++)
					vis[j][k]=viss[x][j][k];
		}
	}
	return;
}

于是TLE87分滚粗


Q:那就只保存要更新的不就行了吗?这样时间复杂度不会炸的呀

A:试过了,我好像打错了。您可以自己试一试。


第三次尝试

此时我发现我一道经典题打了这么久内心已经很崩溃了

(如果在NOIPCSP考场上我就已经87分滚粗了,所以要注意数据范围啊)

仔细看题面和原来的思路,

1.我发现一行不会有两个皇后,所以这是不用存的

2.列用一维数组vis保存即可

3.对角线应该怎么存?

观察题目提供的图

向左下角的一条对角线上的格子行与列的和相等,

向右下角的一条对角线上的格子行与列的差相等

妙啊,解决了解决了。

to_left[i]表示行与列和为i的格子所在的向左下角的对角线上是否已有皇后

to_right[i]表示行与列差为i的格子所在的右下角的对角线上是否已有皇后

且慢——万一行与列的差为负值怎么办

+个n让它变成正数不就好了……

to_right[i+n]表示行与列差为i的格子所在的右下角的对角线上是否已有皇后

行与列差最大为n-1,行与列和最大为2*n

这样我们就知道这两个数组要开多大了

AC代码

#include <bits/stdc++.h>
using namespace std;
bool vis[15],to_left[30],to_right[30];
//向左的同一条对角线上和相等,向右的差相等 
int n,ans[15],cnt;
void dfs(int x){
	if(x==n+1){
		cnt++;
		if(cnt<=3){
			for(int i=1;i<=n;i++)
				printf("%d ",ans[i]);
			printf("\n");//输出前三组解
		}
		return;
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]&&!to_left[x+i]&&!to_right[x-i+n]){//坐标(x,i) 
			vis[i]=1;to_left[x+i]=1;to_right[x-i+n]=1;//标记
			ans[x]=i;
			dfs(x+1);
			vis[i]=0;to_left[x+i]=0;to_right[x-i+n]=0;//回溯
		}
	}
	return;
}
int main() {
	cin>>n;
	dfs(1);
	cout<<cnt<<endl;
	return 0;
}

by cz(不进前n不改名)

qaqaq
原文地址:https://www.cnblogs.com/zdsrs060330/p/13095109.html