5 条题解

  • 18
    @ 2023-8-1 17:50:12

    八皇后问题的解法

    使用了回溯算法。八皇后问题是一个经典的递归问题,在一个8x8的棋盘上放置8个皇后,要求任意两个皇后不能位于同一行、同一列或同一对角线上。

    首先,定义了4个数组a、b、c和d,用来存储皇后的位置信息。数组a表示每行皇后的列数,数组b表示每列是否已经有皇后,数组c和d分别表示两个方向的对角线是否已经有皇后。

    然后,定义了总计数器total和皇后数量n,并且实现了打印函数print(),用来输出满足条件的解。

    接下来是主函数main(),其中首先输入皇后的数量n,然后调用queen(1)函数,开始遍历棋盘上的每一行。在queen函数中,通过循环遍历每一列,判断该位置是否可以放置皇后。如果可以放置,则记录该位置并继续递归调用queen函数,以放置下一行的皇后。当递归到达第n+1行时,表示找到了一个满足条件的解,调用print函数进行输出。

    最后输出结果total,即所有满足条件的解的个数。

    该算法使用了回溯的思想,在搜索过程中剪枝,减少了不必要的搜索空间,从而提高了效率

    #include<iostream>
    using namespace std;
    
    int a[100], b[100], c[100], d[100];
    int total;
    int n;
    
    void print() {
        if (total <= 2) {
            for (int i = 1; i <= n; i++) {
                cout << a[i] << " ";
            }
            cout << endl;
        }
        total++;
    }
    
    void queen(int i) {
        if (i > n) {
            print();
            return;
        }
        
        for (int j = 1; j <= n; j++) {
            if (!b[j] && !c[i+j] && !d[i-j+n]) {
                a[i] = j;
                b[j] = 1;
                c[i+j] = 1;
                d[i-j+n] = 1;
                queen(i+1);
                b[j] = 0;
                c[i+j] = 0;
                d[i-j+n] = 0;
            }
        }
    }
    
    int main() {    
        cin >> n;
        queen(1);
        cout << total;
        return 0;
    }
    
    • @ 2023-8-6 20:20:33

      不要没事发题解,全是错的!!还有,已经有比你厉害的题解了

    • @ 2023-8-6 21:09:08

      油饼吧,这跟老师的代码几乎一样呀,你敢说老师的题解是错的?又或者说那位写出比老师题解还好的题解的“民间高手”没有把自己的题解给老师?那么自私?@

    • @ 2023-8-6 21:10:55

      除了第一小句全都在胡言乱语,“不要没事发题解”不是你胡言乱语的理由@

    • @ 2023-8-6 21:25:28

      @ 请问代码哪里错了,而且题解是用来学习的,不是用来攀比的,难道有比他厉害的题解了,他就不能发了吗

    • @ 2023-8-6 21:25:29

      确实最好要看老师的,但这不代表你可以骂人,人家招你惹你了?@

    • @ 2023-8-8 9:06:43

      人家发题解帮助一下有需要的同学怎么了?再说他也没错呀@

    • @ 2023-9-12 12:18:36

      @ 油饼吧

    • @ 2023-10-22 21:57:00

      @ 神经病,不要理这种人

    • @ 2024-4-27 14:16:14

      @ 罗子曜 油饼吧,人家好心发题解你还骂人家,如果你发题解时别人也这么骂你你会有什么感受!

    • @ 2024-4-27 19:32:48

      @罗子曜你牛你发一个呀!你都没试过,人家是AC!!!你《美国战区导弹防御系统》是针对人家吗???前几次课你也乱喷,人萌新一看,题解区炸了,都是你

    • @ 2024-4-27 19:33:54

      没事不要针对人!!!

  • 9
    @ 2023-10-23 21:52:44

    本文将详细介绍dfs的的应用。

    前置芝士

    深度优先搜索(dfs):

    事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search。

    其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。 举例说明:下图是一个无向图:

    如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是 BB 也可以是 C,DC,D ),则我们可能得到如下的一个访问过程: ABEA\to B \to E没有路了!回溯到 AA ) CFHGD \to C \to F \to H \to G \to D (没有路,最终回溯到 AA , AA 也没有未访问的相邻节点,本次搜索结束)。简要说明深度优先搜索的特点:每次深度优先搜索的结果必然是图的一个连通分量.深度优先搜索可以从多点发起.如果将每个节点在深度优先搜索过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的"拓扑排序",即topological sort。 ——百度百科

    思路分析

    过程

    我们用一个 yy 数组来标记当前这一列有没有被标记(即这一列有没有皇后存在), xie1xie1 数组来标记 / 这样子方向的对角线有没有皇后, xie2xie2 数组来标记 \ 这样子方向的对角线有没有皇后。

    问题来了,列我们好标,但对角线却不好标。

    我们先来看 xie1xie1 数组:

    看来我们只能找规侓了。

    首先先看第一组: (1,1)(1,1)(2,2)(2,2)(3,3)(3,3)(4,4)(4,4)(5,5)(5,5)(6,6)(6,6) 。他们的和不一样,不好找规律,这是我们就不要局限于只看和了,我们看一下差,都是 00 !我们来看一下第二组: (4,1)(4,1)(5,2)(5,2)(6,3)(6,3) ,他们的差是 22 !我们发现,每个对角线中的点的 x,yx,y 坐标的 yxy-x 都是一样且独一无二的!这就是 xie1xie1 数组的规律。

    这是有个问题, yxy-x 有可能是负数,这是我们只需加 nn 就好了(变成 yx+ny-x+n )。

    我们先来看 xie2xie2 数组: 我们画几组样例就会发现它的规律是 x+yx+y

    AC code

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[1000001],cnt;
    bool y[1000001],xie1[10000001],xie2[100000001];
    void dfs(int hang,int dep)
    {
    	if(dep==n+1)
    	{
    		if(cnt>=3)
    		{
    			cnt++;
    			return;
    		}
    		cnt++;
    		for(int i=1;i<=n;i++)   //a数组存答案
    			cout<<a[i]<<' ';
    		cout<<endl;
    		return;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		if(!y[i] && !xie1[i-hang+n] && !xie2[i+hang])
    		{
    			y[i]=1;
    			xie1[i-hang+n]=1;
    			xie2[i+hang]=1;
    			a[dep]=i;
    			dfs(hang+1,dep+1);
    			y[i]=0;
    			xie1[i-hang+n]=0;
    			xie2[i+hang]=0;
    			a[dep]=0;
    		}
    	}
    }
    int main()
    {
    	cin>>n;
    	dfs(1,1);
    	cout<<cnt;
    	return 0;
    }
    
    • 6
      @ 2023-7-23 17:06:33
      思路 非常经典的6皇后问题。关键点在于需要合理标记每一个皇后的位置。 $\\$考虑在位置(i,j)放置皇后以后,以下4类位置就不能再放置皇后: $\\$(1)第i行 $\\$(2)第j列 $\\$(3)从左下到右上的斜行1,其上的棋子坐标应满足行+列的和=i+j。 $\\$(4)从左上到右下的斜行2,其上的棋子坐标应满足行-列的和=i-j。(因为i-j的最小值是-n+1,所以加上一个n,防止数组越界) $\\$前面的题目中,我们只需要标记当前数是否被选取,那么同理在这道题中,我们需要维护4个数组a、b、c、d,分别维护当前行、列、斜行1和斜行2中是否存在数字。 特别的,因为每一行中都需要放置一个皇后,所以a[i]中储存第i行中皇后所在的列数。 $\\$具体代码中,dfs(i)表示当前搜索到第i行。那么当i>n时说明已经搜索结束,输出结果就好了,这里可以自己手写一个print()函数。 $\\$否则需找到一个符合要求的j,要求的是第j列,第i+j斜行1,第i-j+n斜行2之前不能放置皇后,接着将a[i]记录为j,b[j]、c[i+j]、d[i-j+n]都标记为1,接着继续dfs下一行,之后记得需要回溯,把b[j]、c[i+j]、d[i-j+n]都重新标记为0;
      代码
      
      #include<iostream>
      #include <cmath>
      using namespace std;
      int a[100],b[100],c[100],d[100];
      int total;
      int n;
      int print()
      {
          if(total<=2)
          {
              for(int i=1;i<=n;i++)
              cout<<a[i]<<" ";
              cout<<endl;
          }
          total++;
      }
      void queen(int i)
      {
          if(i>n)
          {
              print();
              return;
          }
          else
          {
              for(int j=1;j<=n;j++)
              {
                  if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))
                  {
                      a[i]=j;
                      b[j]=1;
                      c[i+j]=1;
                      d[i-j+n]=1;
                      queen(i+1);
                      b[j]=0;
                      c[i+j]=0;
                      d[i-j+n]=0;
                  }
              }
          }
      }
      int main()
      {    
          cin>>n;
          queen(1);
          cout<<total;
          return 0;
      }
      
      
      • @ 2023-8-6 22:14:54

        print返回为啥要用int? print为啥没有返回?

      • @ 2024-6-15 14:58:43

        /user/13219
        可以用int,就是保险情况下还是用void吧

    • 3
      @ 2024-6-8 16:17:04

      显然,这个题要用DFS 但是,这个题真的必须用DFS吗?👀️ 6 <= n <=13 ∴让人不由自主地想到:打表 于是此蒟蒻开始:

      if(n==6) //打表进行中......
      {
      puts("2 4 6 1 3 5");
      puts("3 6 2 5 1 4");
      puts("4 1 5 2 6 3");
      puts("4");
      }
      if(n==7)
      {
      puts("1 3 5 7 2 4 6");
      puts("1 4 7 3 6 2 5");
      puts("1 5 2 6 3 7 4");
      puts("40");
      }
      if(n==8)
      {
      puts("1 5 8 6 3 7 2 4");
      puts("1 6 8 3 7 4 2 5");
      puts("1 7 4 6 8 2 5 3");
      puts("92");
      }
      if(n==9)
      {
      puts("1 3 6 8 2 4 9 7 5");
      puts("1 3 7 2 8 5 9 4 6");
      puts("1 3 8 6 9 2 5 7 4");
      puts("352");
      }
      if(n==10)
      {
      puts("1 3 6 8 10 5 9 2 4 7");
      puts("1 3 6 9 7 10 4 2 5 8");
      puts("1 3 6 9 7 10 4 2 8 5");
      puts("724");
      }
      if(n==11)
      {
      puts("1 3 5 7 9 11 2 4 6 8 10");
      puts("1 3 6 9 2 8 11 4 7 5 10");
      puts("1 3 7 9 4 2 10 6 11 5 8");
      puts("2680");
      }
      if(n==12)
      {
      puts("1 3 5 8 10 12 6 11 2 7 9 4");
      puts("1 3 5 10 8 11 2 12 6 9 7 4");
      puts("1 3 5 10 8 11 2 12 7 9 4 6");
      puts("14200");
      }
      if(n==13)
      {
      puts("1 3 5 2 9 12 10 13 4 6 8 11 7");
      puts("1 3 5 7 9 11 13 2 4 6 8 10 12");
      puts("1 3 5 7 12 10 13 6 4 2 8 11 9");
      puts("73712");
      }
      return 0;
      }
      

      已AC,我是蒟蒻

      • 2
        @ 2024-2-6 9:15:27
        /*
        	这道题的关键就在于对角线的处理,
        	我采用的方法是:
        	观察发现: 
        	  如果两枚棋子在对角线或是在对角线
        	  的平行线上时,行数之差的绝对值与
        	  列数之差的绝对值相等 
        	所以我便编写了check函数来控制
        	对角线的处理 
        	vis数组的作用是控制列数不相同
        	cnt代表目前是在第几行 
        */
        #include <bits/stdc++.h>
        using namespace std;
        int n,l[15],ans;//l数组存答案,ans表示个数 
        bool vis[15];
        void dfs(int cnt);/*如果写在前面会头重脚轻
        破坏美感 */ 
        bool check(int x,int cnt)
        {
        	for (int i = 1;i < cnt;i++)
        	{
        		if (abs(cnt - i) == abs(x - l[i]))
        		{
        			return 0;
        		}
        	}
        	return 1;
        }/*但为了不太脚重头轻,我把check函数写在了前面
        这样就舒服了*/ 
        int main()
        {
        	cin >> n;
        	dfs(1);
        	cout << ans;
        	return 0;
        }
        void dfs(int cnt)
        {
        	if (cnt == n+1)
        	{
        		ans++;//这一句须写在if前面,先算个数再判断 
        		if (ans <= 3)
        		{
        			for (int i = 1;i <= n;i++)
        			{
        				cout << l[i] << " ";
        			}
        			cout << "\n";
        		}
        		return;/*这里我刚开始忘写了,
        		但居然AC了!!! 
        		让我非常疑惑*/ 
        	}
        	for (int i = 1;i <= n;i++)
        	{
        		if (!vis[i])//列 
        		{
        			if (check(i,cnt))//对角线 
        			{
        				l[cnt] = i;
        				vis[i] = 1;
        				dfs(cnt+1);
        				vis[i] = 0; 
        			}
        		}
        	}
        }/*已AC,请谨慎使用,
        别忘点赞***********************************!!!!!!!!!!!!!!!*/
        
        • 1

        信息

        ID
        332
        时间
        1000ms
        内存
        256MiB
        难度
        1
        标签
        (无)
        递交数
        458
        已通过
        305
        上传者