• 回溯算法(3)--n皇后问题及回溯法相关习题


    一、n皇后问题

    1、概述

            n皇后要求在一个n×n的棋盘上放置n个皇后,使得他们彼此不受攻击,皇后可以攻击同一行、同一列、同一斜线上的敌人,所以n皇后问题要求寻找在棋盘上放置这n个皇后的方案,使得任意两个皇后都不在同一行、同一列或同一斜线上。

    2、子集树解法

    (1)判断是否遍历过叶子结点(t>n),若没有则遍历子集树,判断place(t),即该层从1到n是否存在有成立的情况。

    (2)place剪枝函数,遍历1到t,如果存在一个点满足同一列,同一对角线,那么舍弃这个叶节点,反之保留叶节点。由于每一层保存的点存放在x数组,该数组显性约束了不满足同一行的条件,数组中的索引+1就是行号,数组值为列号。

    (3)若已经满足t>n,即已经遍历过叶子结点,则输出该可行解。

    3、排列树解法

    (1)判断是否遍历过叶子结点(t>n),若没有则遍历排列树,对后续层进行全排列,判断place(t),即该层从1到n是否存在有成立的情况。

    (2)place剪枝函数,此时由于对后续层全排列,第一层的列数不能存在于后续层,不需要添加同一列的检查,其他与子集树一致。

    (3)若已经满足t>n,即已经遍历过叶子结点,则输出该可行解。

    4、代码

    1. //n皇后问题
    2. import java.util.Scanner;
    3. public class Queen {
    4. static int x[]=new int[100];
    5. static int n;
    6. static int sum=0;
    7. public static void main(String [] args)
    8. {
    9. n=new Scanner(System.in).nextInt();
    10. //排列树提前添加x数组值,保证能够进行排列
    11. for(int i=1;i<=n;i++)
    12. x[i]=i;
    13. Backstack2(1);
    14. }
    15. //子集树算法
    16. public static void Backstack(int t)
    17. {
    18. if(t>n)
    19. {
    20. sum++;
    21. for(int i=1;i<=n;i++)
    22. {
    23. for(int j=1;j<=n;j++)
    24. {
    25. if(x[i]==j)
    26. System.out.print("Q ");
    27. else
    28. System.out.print(". ");
    29. }
    30. System.out.println();
    31. }
    32. System.out.println();
    33. }
    34. else
    35. {
    36. for(int i=1;i<=n;i++)
    37. {
    38. x[t]=i;
    39. if(place(t))
    40. Backstack(t+1);
    41. }
    42. }
    43. }
    44. public static boolean place(int t)
    45. {
    46. for(int i=1;i
    47. if((Math.abs(x[i]-x[t])==Math.abs(i-t))||x[i]==x[t])
    48. return false;
    49. return true;
    50. }
    51. //排列树算法
    52. public static void Backstack2(int t)
    53. {
    54. if(t>n)
    55. {
    56. sum++;
    57. for(int i=1;i<=n;i++)
    58. {
    59. for(int j=1;j<=n;j++)
    60. {
    61. if(x[i]==j)
    62. System.out.print("Q ");
    63. else
    64. System.out.print(". ");
    65. }
    66. System.out.println();
    67. }
    68. System.out.println();
    69. }
    70. else{
    71. for(int i=t;i<=n;i++)
    72. {
    73. swap(i,t);
    74. if(place2(t))
    75. Backstack2(t+1);
    76. swap(i,t);
    77. }
    78. }
    79. }
    80. public static void swap(int i,int t)
    81. {
    82. int tmp=x[i];
    83. x[i]=x[t];
    84. x[t]=tmp;
    85. }
    86. public static boolean place2(int t)
    87. {
    88. for(int i=1;i
    89. {
    90. if(Math.abs(x[i]-x[t])==Math.abs(i-t))
    91. return false;
    92. }
    93. return true;
    94. }
    95. }

    5、结果

            通过Q和 . 区分皇后的位置。

    二、含重复元素的全排列

    1、概述

            给定一个可包含重复数字的序列,按任意顺序返回所有不重复的全排列,如:输入【1,1,2】,输出【1,1,2】,【1,2,1】,【2,1,1】。

    2、算法

            排列树问题,在原有问题上添加条件x[t]==x[i]&&t!=i,当前交换的两个数若相同,且不是同一个数,则剪枝,注意循环中使用continue,表示下面的交换和扩展的操作不再进行,但循环继续。

    3、代码

    1. //含重复元素的全排列
    2. import java.util.Scanner;
    3. public class repeatperm {
    4. public static void main(String []args)
    5. {
    6. String m=new Scanner(System.in).nextLine();
    7. String []numbers=m.split("\\s+");
    8. int x[]=new int[numbers.length+1];
    9. for(int i=0;i
    10. x[i+1]=Integer.parseInt(numbers[i]);
    11. perm(x,1);
    12. }
    13. //全排列
    14. public static void perm(int[] x,int t)
    15. {
    16. int n=x.length-1;
    17. if(t>n)
    18. {
    19. for(int i=1;i1;i++)
    20. System.out.print(x[i]+" ");
    21. System.out.println();
    22. }
    23. else
    24. {
    25. for(int i=t;i1;i++)
    26. {
    27. //约束条件,保证不再重复
    28. if(x[t]==x[i]&&t!=i)
    29. continue;
    30. swap(t,i,x);
    31. perm(x,t+1);
    32. swap(t,i,x);
    33. }
    34. }
    35. }
    36. public static void swap(int t,int i,int []x)
    37. {
    38. int tmp=x[i];
    39. x[i]=x[t];
    40. x[t]=tmp;
    41. }
    42. }

    三、 组合

    1、概述

            输入n和k,从1~n中任意不放回抽取k个值的所有情况,输入n=4,k=2,输出【1,2】,【1,3】,【1,4】,【2,3】,【2,4】,【3,4】。

    2、算法

            子集树,在叶子结点未遍历结束前,每一层的值都为上一层的节点值到n之间所有的遍历。

    3、代码

    1. //组合问题
    2. import java.util.Scanner;
    3. public class combination {
    4. public static void main(String[] args)
    5. {
    6. int n=new Scanner(System.in).nextInt(); //n=4
    7. int k=new Scanner(System.in).nextInt(); //k=2
    8. int x[]=new int [k+1];
    9. combine(n,k,1,x);
    10. }
    11. public static void combine(int n,int k,int t,int x[])
    12. {
    13. if(t>k)
    14. {
    15. for(int i=1;i<=k;i++)
    16. System.out.print(x[i]+" ");
    17. System.out.println();
    18. }
    19. else
    20. {
    21. for(int i=x[t-1]+1;i<=n;i++)
    22. {
    23. x[t]=i;
    24. combine(n,k,t+1,x);
    25. }
    26. }
    27. }
    28. }

    四、组合总和

    1、概述

            输入n和k,输出1-9的任取k个数的组合中,组合加和等于n的所有可能情况。

    2、算法

            子集树算法,与上一道题相同,只需要在输出的时候,添加限制总和等于n的输出。另外可以进行改进,对于问题规模过大的情况,如果未完成的组合,组合总和已经大于等于n,则进行剪枝。

    3、代码

    1. //组合总和
    2. import java.util.Scanner;
    3. public class combinationsum {
    4. public static void main(String[] args)
    5. {
    6. int n=new Scanner(System.in).nextInt(); //n=7,判断条件和
    7. int k=new Scanner(System.in).nextInt(); //k=3
    8. int x[]=new int [k+1];
    9. combine(n,k,1,x);
    10. }
    11. public static void combine(int n,int k,int t,int x[])
    12. {
    13. if(t>k)
    14. {
    15. int sum=0;
    16. for(int j:x)
    17. sum+=j;
    18. if(sum==n)
    19. {
    20. for(int i=1;i<=k;i++)
    21. System.out.print(x[i]+" ");
    22. System.out.println();
    23. }
    24. }
    25. else
    26. {
    27. for(int i=x[t-1]+1;i<=9;i++)
    28. {
    29. x[t]=i;
    30. combine(n,k,t+1,x);
    31. }
    32. }
    33. }
    34. }

  • 相关阅读:
    docker常用命令
    【C++】topk问题
    base64_encode()和base64_decode(),URL的加密解密详解
    淘宝资深java技术专家整理分享java异步编程实战文档
    cefpython3的使用
    关于git flow工作流的使用
    计算机毕业设计 基于微信小程序的学习资料销售平台的设计与实现 Java实战项目 附源码+文档+视频讲解
    萤石网络发布家用及商用清洁机器人 积极布局具身智能
    【libGDX】加载G3DJ模型
    1688普货98%覆盖率,一键生成采购订单,轻松提升采购效率!
  • 原文地址:https://blog.csdn.net/m0_60177079/article/details/134479555