• DFS之剪枝与优化


    剪枝

    1.优化搜索顺序:在大部分情况下,我们应该优先搜索分支较少的结点

    2.排除等效冗余(在不考虑顺序的情况下,尽量用组合的方式来搜索)

    3.可行性剪枝

    4.最优性剪枝

    5.记忆化搜索 

    165. 小猫爬山 - AcWing题库

    1. import java.util.*;
    2. public class Main{
    3. static int N = 20;
    4. static int n, m, res = N;
    5. static int[] w = new int[N];//每只小猫的数量
    6. static int[] sum = new int[N];//每个缆车已经放的小猫的重量之和
    7. public static void dfs(int u, int k){//u猫的数量,共有k个缆车
    8. //最优性剪枝
    9. if(k >= res) return;
    10. if(u == n){
    11. res = k;
    12. return;
    13. }
    14. //能在以前的车里面找到位置
    15. for(int i = 0; i < k; i ++){
    16. if(sum[i] + w[u] <= m){//可行性剪枝
    17. sum[i] += w[u];
    18. dfs(u + 1, k);
    19. sum[i] -= w[u];//回溯,恢复现场
    20. }
    21. }
    22. //新开一辆车
    23. sum[k] = w[u];//数组是从0开始的,所以这里的第k+1辆车用k表示
    24. dfs(u + 1, k + 1);
    25. sum[k] = 0;//恢复现场
    26. }
    27. public static void main(String[] args){
    28. Scanner sc = new Scanner(System.in);
    29. n = sc.nextInt();
    30. m = sc.nextInt();
    31. for(int i = 0; i < n; i ++){
    32. w[i] = sc.nextInt();
    33. }
    34. //优化搜索顺序,分支少的先搜索(重猫)
    35. Arrays.sort(w, 0, n);//排序
    36. int[] b = new int[n];
    37. for(int i = n - 1, j = 0; i >= 0; i --){
    38. b[j ++] = w[i];
    39. }
    40. for(int i = 0; i < n; i ++) w[i] = b[i];
    41. dfs(0, 0);
    42. System.out.print(res);
    43. }
    44. }

     

    166. 数独 - AcWing题库

    可以用一个九位的二进制数来表示一行的状态 

    1. import java.util.*;
    2. public class Main{
    3. static int N = 9, cnt;
    4. static char[] str;//输入输出
    5. static int[] row = new int[N];//
    6. static int[] cel = new int[N];//
    7. static int[][] cell = new int[3][3];//九宫格
    8. static int[] ones = new int[1 << N];//二进制状态中1的个数
    9. //二进制数中最后一个1的位置
    10. static Map<Integer, Integer> map = new HashMap<>();
    11. //初始化所有行列和九宫格都可以填19中的数
    12. public static void init(){
    13. for(int i = 0; i < N; i ++) row[i] = cel[i] = (1 << N) - 1;
    14. for(int i = 0; i < 3; i ++){
    15. for(int j = 0; j < 3; j ++){
    16. cell[i][j] = (1 << N) - 1;
    17. }
    18. }
    19. }
    20. //st为true表示在这个位置上填上一个t,在这个位置上去除t
    21. public static void draw(int x, int y, int t, boolean st){
    22. if(st) str[x * N + y] = (char)(t + '1');
    23. else str[x * N + y] = '.';
    24. int v = 1 << t;//第几位的1
    25. if(!st) v = -v;//回溯
    26. row[x] -= v;
    27. cel[y] -= v;
    28. cell[x / 3][y / 3] -= v;
    29. }
    30. //lowbit函数求二进制数的最后一个1
    31. public static int lowbit(int x){
    32. return x & -x;
    33. }
    34. //获取他能够放的所有的状态为1的位置
    35. public static int get(int x, int y){
    36. return row[x] & cel[y] & cell[x / 3][y / 3];
    37. }
    38. public static boolean dfs(int cnt){
    39. if(cnt == 0) return true;//如果空点已经没有了
    40. int minv = 10;//能填的数的个数
    41. int x = -1, y = -1;//初始化这个点的横纵坐标
    42. //找到能填的数最少的点
    43. for(int i = 0; i < N; i ++){
    44. for(int j = 0; j < N; j ++){
    45. if(str[i * N + j] == '.'){//空着的点才能填
    46. int f = get(i, j);//获取它能够放的状态
    47. if(ones[f] < minv){
    48. minv = ones[f];
    49. x = i;
    50. y = j;
    51. }
    52. }
    53. }
    54. }
    55. int state = get(x, y);//找到分支最小的那一点
    56. for(int i = state; i != 0; i -= lowbit(i)){
    57. int t = map.get(lowbit(i));
    58. draw(x, y, t, true);//进行放置
    59. if(dfs(cnt - 1)) return true;
    60. draw(x, y, t, false);//回溯
    61. }
    62. return false;//不行就返回false
    63. }
    64. //main函数
    65. public static void main(String[] args){
    66. Scanner sc = new Scanner(System.in);
    67. //获取每个状态中1的个数
    68. for(int i = 0; i < 1 << N; i ++){
    69. for(int j = 0; j < N; j ++){
    70. ones[i] += i >> j & 1;
    71. }
    72. }
    73. //二进制数中最后一个1的位置
    74. for(int i = 0; i < N; i ++) map.put(1 << i, i);
    75. while(true){
    76. String s = sc.next();//输入数据
    77. if(s.equals("end")) break;
    78. str = s.toCharArray();//转化为数组
    79. init();//初始化
    80. cnt = 81;//一共有81个空位要填
    81. for(int i = 0; i < N; i ++){
    82. for(int j = 0; j < N; j ++){
    83. //转化为二维数组来看
    84. if(str[i * N + j] != '.'){
    85. draw(i, j, str[i * N + j] - '1', true);//填上这个数
    86. cnt --;//减去空着的点数
    87. }
    88. }
    89. }
    90. dfs(cnt);
    91. //把char数组转化为字符串输出
    92. System.out.println(new String(str));
    93. }
    94. }
    95. }

     

    167. 木棒 - AcWing题库 

    1. import java.util.*;
    2. public class Main{
    3. static int N = 70;
    4. static int length, sum, n;
    5. static int[] w = new int[N];//每根小棍的长度
    6. static boolean[] st = new boolean[N];//标记这个小棍用过了没有
    7. public static boolean dfs(int u, int s, int start){//第几根大棍,这个大棍的长度,从第几根小棍开始枚举
    8. if(u * length == sum) return true;
    9. if(s == length) return dfs(u + 1, 0, 0);//如果当前大棍已经满了
    10. for(int i = start; i < n; i ++){
    11. if(st[i]) continue;//已经用过了
    12. //可行性剪枝
    13. if(s + w[i] > length) continue;//超过大小了
    14. st[i] = true;//标记一下
    15. if(dfs(u, s + w[i], i + 1)) return true;
    16. st[i] = false;//恢复现场
    17. if(s == 0) return false;//放在第一个位置失败
    18. if(s + w[i] == length) return false;//放在最后一个位置失败
    19. int j = i;
    20. while(j < n && w[i] == w[j]) j ++;
    21. i = j - 1;
    22. }
    23. return false;
    24. }
    25. public static void main(String[] args){
    26. Scanner sc = new Scanner(System.in);
    27. while(true){
    28. length = 0;
    29. sum = 0;
    30. Arrays.fill(st, false);//多组测试数据
    31. n = sc.nextInt();
    32. if(n == 0) break;
    33. for(int i = 0; i < n; i ++){
    34. w[i] = sc.nextInt();
    35. length = Math.max(length, w[i]);
    36. sum += w[i];//所有大棍的总长度
    37. }
    38. //从大到小排序,优化搜索顺序
    39. Arrays.sort(w, 0, n);
    40. int[] b = new int[n];
    41. for(int i = n - 1, j = 0; i >= 0; i --){
    42. b[j ++] = w[i];
    43. }
    44. for(int i = 0; i < n; i ++){
    45. w[i] = b[i];
    46. }
    47. while(true){
    48. //判断总长度是不是length的倍数
    49. if(sum % length == 0 && dfs(0, 0, 0)){
    50. System.out.println(length);//因为length从小到大枚举,所以第一次成立的时候就是最小值
    51. break;
    52. }
    53. length ++;
    54. }
    55. }
    56. }
    57. }

     

    168. 生日蛋糕 - AcWing题库

     

    还不是太懂,先跳一下 

  • 相关阅读:
    内存空间扩充之进程覆盖技术,交换技术
    汇编语言与微机原理 期末复习题整理(小题)
    重学操作系统(一)操作系统概述
    《Go 简易速速上手小册》第8章:网络编程(2024 最新版)
    Nginx 文件名逻辑漏洞(CVE-2013-4547)(Vulhub)
    Codeforces Round 900 (Div. 3) 题解 | JorbanS
    JNI相关总结
    深入理解 Netty FastThreadLocal
    【已解决】chrome视频无法自动播放的问题
    KILM: Knowledge Injection into Encoder-Decoder Language Models
  • 原文地址:https://blog.csdn.net/m0_73165551/article/details/136367390