zoukankan      html  css  js  c++  java
  • 回溯 八皇后问题 与 0-1背包

    八皇后问题描述:

    我们有一个 8x8 的棋盘,希望往里放 8 个棋子(皇后),每个棋子所在的行、列、对角线都不能有另一个棋子。你可以看我画的图,第一幅图是满足条件的一种方法,第二幅图是不满足条件的。八皇后问题就是期望找到所有满足这种要求的放棋子方式。

     我们把这个问题划分成 8 个阶段,依次将 8 个棋子放到第一行、第二行、第三行……第八行。在放置的过程中,我们不停地检查当前放法,是否满足要求。如果满足,则跳到下一行继续放置棋子;如果不满足,那就再换一种放法,继续尝试。

    回溯算法非常适合用递归代码实现,所以,我把八皇后的算法翻译成代码:

    public class Queens8 {
    
        int[] result = new int[8];//全局或成员变量,下标表示行,值表示queen存储在哪一列
    
        public static void main(String[] args) {
            new Queens8().cal8queens(0);
    
        }
        public void cal8queens(int row) { // 调用方式:cal8queens(0);
            if (row == 8) { // 8个棋子都放置好了,打印结果
                printQueens(result);
                return; // 8行棋子都放好了,已经没法再往下递归了,所以就return
            }
            for (int column = 0; column < 8; ++column) { // 每一行都有8中放法
                // 这里是分叉路,分岔路下面递归进去执行完后,会退回这里
                // 这里作为最外层的递归起始点,row一直是0,不过是colum一直在移动,寻找各种符合的条件
                // 比如 result[0] = 0,第一个起点,下面有多少种情况,
                // 第二个起点result[0] = 1 还需要用递归判断下面有多少种情况
                if (isOk(row, column)) { // 有些放法不满足要求
                    result[row] = column; // 第row行的棋子放到了column列
                    System.out.println("row="+row+"	colum="+column);
                    cal8queens(row+1); // 考察下一行
                }
                System.out.println("------------colum="+column);
            }
        }
    
        private boolean isOk(int row, int column) {//判断row行column列放置是否合适
            int leftup = column - 1, rightup = column + 1;
            for (int i = row-1; i >= 0; --i) { // 逐行往上考察每一行
                if (result[i] == column) return false; // 第i行的column列有棋子吗?
                if (leftup >= 0) { // 考察左上对角线:第i行leftup列有棋子吗?
                    if (result[i] == leftup) return false;
                }
                if (rightup < 8) { // 考察右上对角线:第i行rightup列有棋子吗?
                    if (result[i] == rightup) return false;
                }
                --leftup; ++rightup;
            }
            return true;
        }
    
        private void printQueens(int[] result) { // 打印出一个二维矩阵
            for (int row = 0; row < 8; ++row) {
                for (int column = 0; column < 8; ++column) {
                    if (result[row] == column) System.out.print("Q ");
                    else System.out.print("* ");
                }
                System.out.println();
            }
            System.out.println();
        }
    }

    0-1背包问题

    我们有一个背包,背包总的承载重量是 Wkg。现在我们有 n 个物品,每个物品的重量不等,并且不可分割。我们现在期望选择几件物品,装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大?

    为什么叫0-1?应为物品是不可分割的,要么装要么不装。

    1.确定好最终返回条件

    2.转移公式

    具体例子:小明有个背包,最大装9kg物品,现在桌上有几个物品,重量(kg)分别是2,2,4,6,3  问,小明包里能装的最大多少kg?

    private static  int[] weight = {2,2,4,6,3};  // 物品重量
    private static int n = 5; // 物品个数
    private static int w = 9; // 背包承受的最大重量
    代码如下:
    public int maxW = Integer.MIN_VALUE; //存储背包中物品总重量的最大值
    // cw表示当前已经装进去的物品的重量和;i表示考察到哪个物品了;
    // w背包重量;weight表示每个物品的重量;n表示物品个数
    // 假设背包可承受重量9,物品个数5,物品重量存储在数组weight中,那可以这样调用函数:
    // f(0, 0, weight, 5, 9)
    public void f(int i, int cw, int[] items, int n, int w) {
      if (cw == w || i == n) { // cw==w表示装满了;i==n表示已经考察完所有的物品 
        if (cw > maxW) maxW = cw;
        return;
      }
      f(i+1, cw, items, n, w);    //这里递归只写cw,说明这行代码不装物品
      if (cw + items[i] <= w) {// 已经超过可以背包承受的重量的时候,就不要再装了
        f(i+1,cw + items[i], items, n, w);//这里要装这个物品,但是需要上面的约束条件
      }
    }

     回溯可以添加备忘录做优化

    优化后如下:

    private int maxW = Integer.MIN_VALUE; // 结果放到maxW中
    private int[] weight = {2,2,4,6,3};  // 物品重量
    private int n = 5; // 物品个数
    private int w = 9; // 背包承受的最大重量
    private boolean[][] mem = new boolean[5][10]; // 备忘录,默认值false
    public void f(int i, int cw) { // 调用f(0, 0)
      if (cw == w || i == n) { // cw==w表示装满了,i==n表示物品都考察完了
        if (cw > maxW) maxW = cw;
        return;
      }
      if (mem[i][cw]) return; // 重复状态
      mem[i][cw] = true; // 记录(i, cw)这个状态
      f(i+1, cw); // 选择不装第i个物品
      if (cw + weight[i] <= w) {
        f(i+1,cw + weight[i]); // 选择装第i个物品
      }
    }
    View Code
  • 相关阅读:
    TCP流量控制,拥塞控制原理
    Java数组--求一个数组中连续m个数的和最大的数组组合
    一次使用IDEA编写JDK动态代理Class数组中有关泛型的问题
    Java数组--一个整型数组,给定一个定数,求数组中两个数的和与定数相等
    Java基础知识--Stream接口的理解与应用
    JSAP107
    JSAP106
    JSAP105
    JSAP104
    JSAP103
  • 原文地址:https://www.cnblogs.com/yanghaolie/p/13841421.html
Copyright © 2011-2022 走看看