洛谷 P1219. 八皇后--dfs两种写法

[USACO1.5] 八皇后 Checker Challenge

题目描述

一个如下的 \(6 \times 6\) 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 \(2\ 4\ 6\ 1\ 3\ 5\) 来描述,第 \(i\) 个数字表示在第 \(i\) 行的相应位置有一个棋子,如下:

行号 \(1\ 2\ 3\ 4\ 5\ 6\)

列号 \(2\ 4\ 6\ 1\ 3\ 5\)

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 \(3\) 个解。最后一行是解的总个数。

输入格式

一行一个正整数 \(n\),表示棋盘是 \(n \times n\) 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

样例 #1

样例输入 #1

6

样例输出 #1

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

提示

【数据范围】
对于 \(100\%\) 的数据,\(6 \le n \le 13\)

题目翻译来自NOCOW。

USACO Training Section 1.5

题解

1

从前往后枚举所有点放皇后的情况 + 剪枝 优化掉一层对行的枚举 优化对正反对角线的枚举 利用行和列去表示对角线

#include <bits/stdc++.h>

using namespace std;

const int N = 30; // 13 * 2 - 1

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

void dfs(int u)
{
    if (u > n)
    {
        cnt ++ ;
        if(cnt <= 3)
        {
            for (int i = 1; i <= n; i ++ )
            {
                for (int j = 1; j <= n; j ++ )
                {
                    if (g[i][j] == 'Q')
                    {
                        printf("%d ", j);
                    }
                }

            }
            cout << endl;
        }
        return;
    }
    for (int i = 1; i <= n; i ++ )
    {
        if (!col[i] && !dg[u + i] && !udg[n - u + i]) //分别对应 列 对角线和反对角线 
        {
            col[i] = dg[u + i] = udg[n - u + i] = true;
            g[u][i] = 'Q';
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n - u + i] = false;
            g[u][i] = '.';
        }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= n; j ++ )
        {
            g[i][j] = '.';
        }
    }
    dfs(1);
    printf("%d\n", cnt);
    return 0;
}

2

这个搜索方案没有办法确保按照字典序输出棋盘
这种搜索方法就不用我们用循环去枚举列数了 一个点一个点的搜 每个点两种情况 放皇后和不放皇后

#include <bits/stdc++.h>

using namespace std;

const int N = 30; // 13 * 2 - 1

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

void dfs(int x, int y, int s) //行数 列数 当前皇后个数 
{
    if (x > n) //当前行枚举完 换到第二行第一个格子开始枚举 
    {
        y ++;
        x = 1;
    }
    if (y > n)
    {
        if (s == n)
        {
            //cout << 1;
            cnt ++ ;

            {
                for (int i = 1; i <= n; i ++ )
                {
                    for (int j = 1; j <= n; j ++ )
                    {
                        if (g[i][j] == 'Q')
                            printf("%d ", j);
                    }
                }
                cout << endl;
            }
        }
        return; //一定要放在if(s == n)判断的外面 只要这种情况搜完了 不管是否是合法情况 都要返回 
    }

    //该格子不放皇后
    g[x][y] = '.';
    dfs(x + 1, y, s); 


    //该格子放皇后 
    if (!row[x] && !col[y] && !dg[x + y] && !udg[n - x + y]) 
    {
        row[x] = col[y] = dg[x + y] = udg[n - x + y] = true;
        g[x][y] = 'Q';
        dfs(x + 1, y, s + 1);
        row[x] = col[y] = dg[x + y] = udg[n - x + y] = false;
        g[x][y] = '.';
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= n; j ++ )
        {
            g[i][j] = '.';
        }
    }
    dfs(1, 1, 0);
    printf("%d\n", cnt);
    return 0;
}
posted @ 2024-04-16 16:11  MsEEi  阅读(15)  评论(0)    收藏  举报