LeetCode 题解 | 51. N皇后(DFS回溯 C++)

题目描述(困难难度)

原题链接

在这里插入图片描述

LeetCode代码

class Solution {
public:
    static const int N = 30;
    char g[N][N];
    bool col[N], dg[N], udg[N];
   
    vector<vector<string>> ans;

    void dfs(int u, int n) {
        if (u == n) { // 输出答案,如果是修改原数组,需用bool型dfs
            vector<string> sol; // sol每次都要更新
            for(int i = 1; i <= n; i ++) {
                string tmp = "";
                for (int j = 1; j <= n; j ++) {
                    tmp += g[i][j];
                }
                sol.push_back(tmp);
            }
            ans.push_back(sol);
            return;
        }

        for (int i = 1; i <= n; i ++) {
            if (!col[i] && !dg[u + i - 1] && !udg[u - i + n]) {
                g[u + 1][i] = 'Q';                               // 从u = 1开始填
                col[i] = dg[u + i - 1] = udg[u - i + n] = true;
                dfs(u + 1, n);
                g[u + 1][i] = '.';                               // 恢复现场,这步必须有,因为这题不像排列数字那题都能被覆盖到
                col[i] = dg[u + i - 1] = udg[u - i + n] = false; // 恢复现场,不影响下一个分支的搜索,这步很关键
            }
        }
    }

    vector<vector<string>> solveNQueens(int n) {
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= n ; j ++) {
                g[i][j] = '.';
            }
        }
        dfs(0, n);
        return ans;
    }
};

AcWing 843. n-皇后问题

算法1

(按行枚举) O ( n ! ) O(n!)

说明:对角线dg[u + i],反对角线udg[n - u + i]中的下标表示的是截距

(u, i)即(x, y)

  • 对角线y = x + b, 截距b = y - x
    因为我们要把b当做数组下标,所以b不能是负的,所以我们+n,保证是结果是正的

  • 反对角线y = -x + b, 截距是b = y + x

核心目的找一些合法的下标来表示dg或udg是否被标记过就可以了,所以如果你愿意,你取udg[n + n - u + i]也没有事情,只要所有(u, i)对可以映射过去就行

C++ 代码


#include <iostream>
using namespace std;
const int N = 20;
 
// bool数组用来判断搜索的下一个位置是否可行
// col列,dg对角线,udg反对角线
// g[N][N]用来存路径

int n;
char g[N][N];
bool col[N], dg[N], udg[N];

void dfs(int u)
{
	// u == n 表示已经搜了n行,故输出这条路径
    if (u == n)
    {
        for (int i = 0; i < n; i ++ ) puts(g[i]);   // 等价于cout << g[i] << endl;
        puts("");  // 换行
        return;
    }
    
    //对n个位置按行搜索
    for (int i = 0; i < n; i ++ )
		// 剪枝(对于不满足要求的点,不再继续往下搜索)              
		// udg[n - u + i],+n是为了保证大于0
		// 只要保证(u,i)都能映射到,+2n也没事
        if (!col[i] && !dg[u + i] && !udg[n - u + i])
        {
            g[u][i] = 'Q';
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs(u + 1);
            // 恢复现场 这步很关键
            col[i] = dg[u + i] = udg[n - u + i] = false;
			g[u][i] = '.';
            
        }
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            g[i][j] = '.';
    
    dfs(0);
    
    return 0;
}	

算法2

(按每个元素枚举) O ( 2 n 2 ) O(2^{n^2} )

时间复杂度分析:每个位置都有两种情况,总共有 n 2 n^2 个位置

C++ 代码

// 不同搜索顺序 时间复杂度不同  所以搜索顺序很重要!
#include <iostream>
using namespace std;
const int N = 20;

// 因为是一个个搜索,所以加了row
int n;
char g[N][N];
bool row[N], col[N], dg[N], udg[N];

// s表示已经放上去的皇后个数
void dfs(int x, int y, int s)
{
	// 处理超出边界的情况
    if (y == n) y = 0, x ++ ;
    
	// 说明已经放好了n个皇后,表示枚举完 n^2 个了
    if (x == n)
    {
        if (s == n)
        {
            for (int i = 0; i < n; i ++ ) puts(g[i]);
            puts("");
        }
        return;
    }
    
    // 不放皇后  就往下搜下一个位置
    dfs(x, y + 1, s);
    
    // 放皇后
    if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
    {
        g[x][y] = 'Q';
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
        dfs(x, y + 1, s + 1);
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
        g[x][y] = '.';
    }
}


int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            g[i][j] = '.';
    
    dfs(0, 0, 0);
    
    return 0;
}

写在最后:我的博客主要是对计算机领域所学知识的总结、回顾和思考,把每篇博客写得通俗易懂是我的目标,分享技术和知识是一种快乐 ,非常欢迎大家和我一起交流学习,有任何问题都可以在评论区留言,也期待与您的深入交流(^∀^●)

发布了248 篇原创文章 · 获赞 89 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/104394163
今日推荐