zoukankan      html  css  js  c++  java
  • 题解 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
  • 相关阅读:
    OC
    OC
    核心动画
    核心动画
    核心动画
    数据存储1
    plsql语句基础
    Oracle3连接&子查询&联合查询&分析函数
    oracle2约束添加&表复制&拼接
    Oracle表空间创建及表创建
  • 原文地址:https://www.cnblogs.com/zdsrs060330/p/13095109.html
Copyright © 2011-2022 走看看