装载问题
代码(附解题思路)
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Load {
public static Scanner scanner = new Scanner(System.in);
public static int weight1; //第一个集装箱的载重能力
public static int weight2; //第二个集装箱的载重能力
public static int n; // n个集装箱
public static int maxWeight=0; //做大载重量
public static int sum1 = 0, sumAll = 0; //sum1表示第一个集装箱的重量,sumAll表示所有集装箱的重量
public static int[] items; //记录集装箱的重量
public static int []record;
public static int []bestRecord;
public static void main(String args[]) {
n = scanner.nextInt();
items = new int[n];
bestRecord=new int[n];
record=new int[n];
weight1 = scanner.nextInt();
weight2 = scanner.nextInt();
for (int i = 0; i < n; i++) {
items[i] = scanner.nextInt();
}
backtrack(0);
System.out.println(maxWeight);
// System.out.println(record);
for (int i = 0; i < n; i++) {
System.out.print(bestRecord[i] + " ");
}
}
//回溯求解
public static void backtrack(int i) {
//集装箱超载
if(sum1>weight1){
return;
}
// 当前已经到达叶子节点
if(i==n){
// System.out.println(record);
// record.remove(record.size()-1);
if(sumAll-sum1<=weight2){ //能全部装下
if(sum1>maxWeight){
for (int j = 0; j < n; j++) {
bestRecord[j]=record[j];
}
maxWeight=sum1;
}
}
return;
}
//左子树遍历,装当前货物到第一个集装箱
sum1+=items[i];
record[i]=1;
backtrack(i+1);
//右子树遍历,当前货物不装到第一个集装箱
sum1-=items[i];
record[i]=0;
backtrack(i+1);
}
}
运行结果
输入
5
10 10
20 3 5 6 8
输出
9
0 1 0 1 0
8皇后问题
代码(附思路)
import java.util.Scanner;
public class Queue {
static int num;
static Scanner scanner = new Scanner(System.in);
public static void main(String args[]) {
int[] x = new int[8]; //下标代表第几行,值代表放在该行的哪一个位置
int n = 8;
for (int i = 0; i < n; i++) {
x[i] = -1; //初始化每个位置都没放皇后
}
Bhh(0, 8, x);
System.out.println("共有:" + num + "种组合");
}
private static void Bhh(int k, int n, int[] x) {
for (int i = 0; i < n; i++) {
if (Wzhi(k, i, x)) {
x[k] = i;
if (k == n - 1) {
for (int j = 0; j < n; j++) {
{
System.out.print(x[j] + " ");
}
}
num++;
System.out.println();
} else {
Bhh(k + 1, n, x);
}
}
}
}
private static boolean Wzhi(int k, int i, int[] x) {
for (int j = 0; j < k; j++) {
//判断是否同一列,判断是否在对角线上(利用两直角边长度相同)
if ((i == x[j]) || (Math.abs((k - j)) == Math.abs(i - x[j]))) {
return false;
}
}
return true;
}
}
运行结果
输出
0 4 7 5 2 6 1 3
0 5 7 2 6 3 1 4
0 6 3 5 7 1 4 2
0 6 4 7 1 3 5 2
1 3 5 7 2 0 6 4
1 4 6 0 2 7 5 3
1 4 6 3 0 7 5 2
1 5 0 6 3 7 2 4
1 5 7 2 0 3 6 4
1 6 2 5 7 4 0 3
1 6 4 7 0 3 5 2
1 7 5 0 2 4 6 3
2 0 6 4 7 1 3 5
2 4 1 7 0 6 3 5
2 4 1 7 5 3 6 0
2 4 6 0 3 1 7 5
2 4 7 3 0 6 1 5
2 5 1 4 7 0 6 3
2 5 1 6 0 3 7 4
2 5 1 6 4 0 7 3
2 5 3 0 7 4 6 1
2 5 3 1 7 4 6 0
2 5 7 0 3 6 4 1
2 5 7 0 4 6 1 3
2 5 7 1 3 0 6 4
2 6 1 7 4 0 3 5
2 6 1 7 5 3 0 4
2 7 3 6 0 5 1 4
3 0 4 7 1 6 2 5
3 0 4 7 5 2 6 1
3 1 4 7 5 0 2 6
3 1 6 2 5 7 0 4
3 1 6 2 5 7 4 0
3 1 6 4 0 7 5 2
3 1 7 4 6 0 2 5
3 1 7 5 0 2 4 6
3 5 0 4 1 7 2 6
3 5 7 1 6 0 2 4
3 5 7 2 0 6 4 1
3 6 0 7 4 1 5 2
3 6 2 7 1 4 0 5
3 6 4 1 5 0 2 7
3 6 4 2 0 5 7 1
3 7 0 2 5 1 6 4
3 7 0 4 6 1 5 2
3 7 4 2 0 6 1 5
4 0 3 5 7 1 6 2
4 0 7 3 1 6 2 5
4 0 7 5 2 6 1 3
4 1 3 5 7 2 0 6
4 1 3 6 2 7 5 0
4 1 5 0 6 3 7 2
4 1 7 0 3 6 2 5
4 2 0 5 7 1 3 6
4 2 0 6 1 7 5 3
4 2 7 3 6 0 5 1
4 6 0 2 7 5 3 1
4 6 0 3 1 7 5 2
4 6 1 3 7 0 2 5
4 6 1 5 2 0 3 7
4 6 1 5 2 0 7 3
4 6 3 0 2 7 5 1
4 7 3 0 2 5 1 6
4 7 3 0 6 1 5 2
5 0 4 1 7 2 6 3
5 1 6 0 2 4 7 3
5 1 6 0 3 7 4 2
5 2 0 6 4 7 1 3
5 2 0 7 3 1 6 4
5 2 0 7 4 1 3 6
5 2 4 6 0 3 1 7
5 2 4 7 0 3 1 6
5 2 6 1 3 7 0 4
5 2 6 1 7 4 0 3
5 2 6 3 0 7 1 4
5 3 0 4 7 1 6 2
5 3 1 7 4 6 0 2
5 3 6 0 2 4 1 7
5 3 6 0 7 1 4 2
5 7 1 3 0 6 4 2
6 0 2 7 5 3 1 4
6 1 3 0 7 4 2 5
6 1 5 2 0 3 7 4
6 2 0 5 7 4 1 3
6 2 7 1 4 0 5 3
6 3 1 4 7 0 2 5
6 3 1 7 5 0 2 4
6 4 2 0 5 7 1 3
7 1 3 0 6 4 2 5
7 1 4 2 0 6 3 5
7 2 0 5 1 4 6 3
7 3 0 2 5 1 6 4
共有:92种组合
实验总结
回溯法:把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解。
回溯法按深度优先策略搜索问题的解空间树。首先从根节点出发搜索解空间树,当算法搜索至解空间树的某一节点时,先利用剪枝函数判断该节点是否可行(即能得到问题的解)。如果不可行,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索。
回溯法的基本行为是搜索,搜索过程使用剪枝函数来为了避免无效的搜索。剪枝函数包括两类:1. 使用约束函数,剪去不满足约束条件的路径;2.使用限界函数,剪去不能得到最优解的路径。
问题的关键在于如何定义问题的解空间,转化成树(即解空间树)。解空间树分为两种:子集树和排列树。两种在算法结构和思路上大体相同。