两道关于回溯法,分支限界法的算法题
1.最小重量机器设计问题:设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设 wij 是从供应商j处购得的部件 i 的重量, cij 是相应的价格。试设计一个算法,给出总价格不超过 c 的最小重量机器设计。
方法一:回溯法设计:
import static org.junit.Assert.*; import java.util.Scanner; import org.junit.Test; public class 最小重量机器 { /* * 3 3 * 3 2 1 4 5 6 * 1 4 3 2 5 6 * 5 6 3 2 1 4 * 10 * * 答案是5 */ private static int w[][]; private static int c[][]; private static int vis[][]; private static int n, m, cc; private static int ans; private static Scanner cin; static{ cin = new Scanner(System.in); } private static void init() { for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ vis[i][j] = 0; } } ans = Integer.MAX_VALUE; } public static void main(String[] args) { System.out.println("请输入部件数目以及供货商数量n和m:"); n = cin.nextInt(); m = cin.nextInt(); w = new int[n][m]; c = new int[n][m]; vis = new int[n][m]; System.out.println("n行代表n个部件,每行输入每个供货商供应此部件的重量以及价格:"); for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ w[i][j] = cin.nextInt(); c[i][j] = cin.nextInt(); } } System.out.println("请输入不超过的价格p:"); cc = cin.nextInt(); init(); work(0, cc, 0, 0); if(ans == Integer.MAX_VALUE){ System.out.println("不能找出总价格不超过 c的最小重量机器的方案"); }else{ System.out.println("满足方案的最小重量是:" + ans); } } private static void work(int i, int cc, int ww, int p){ if(i == n){ if(p <= cc){ ans = Math.min(ans, ww); } return; } for(int j = 0; j < m; j++){ vis[i][j] = 1; work(i + 1, cc, ww + w[i][j], p + c[i][j]); vis[i][j] = 0; } } }
分支限界法:
import static org.junit.Assert.*; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import javax.management.Query; import org.junit.Test; class Node{ private int curw, curv; private int units_i; private int[] route; Node(){ route = new int[110]; } public Node(int curw, int curv, int units_i, int[] route) { super(); this.curw = curw; this.curv = curv; this.units_i = units_i; this.route = route; } public int getCurw() { return curw; } public void setCurw(int curw) { this.curw = curw; } public int getCurv() { return curv; } public void setCurv(int curv) { this.curv = curv; } public int getUnits_i() { return units_i; } public void setUnits_i(int units_i) { this.units_i = units_i; } public int[] getRoute() { return route; } public void setRoute(int[] route) { this.route = route; } } public class 最小重量设计_分支限界法 { /* * 3 3 * 3 2 1 4 5 6 * 1 4 3 2 5 6 * 5 6 3 2 1 4 * 10 * * 答案是5 */ private static int w[][]; private static int c[][]; private static int n, m, cc; private static Scanner cin; private static Queue<Node>q; static{ cin = new Scanner(System.in); q = new LinkedList<Node>(); } public static void main(String[] args) { System.out.println("请输入部件数目以及供货商数量n和m:"); n = cin.nextInt(); m = cin.nextInt(); w = new int[n][m]; c = new int[n][m]; System.out.println("n行代表n个部件,每行输入每个供货商供应此部件的重量以及价格:"); for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ w[i][j] = cin.nextInt(); c[i][j] = cin.nextInt(); } } System.out.println("请输入不超过的价格p:"); cc = cin.nextInt(); q.clear(); int[] route = new int[110]; Node e = new Node(0, 0, -1, route); q.add(e); int ans = Integer.MAX_VALUE; while(!q.isEmpty()){ e = q.poll(); for(int j = 0; j < m; j++){ int i = e.getUnits_i() + 1; if(i >= n){ if(e.getCurv() <= cc){ if(ans > e.getCurw()){ ans = Math.min(ans, e.getCurw()); route = e.getRoute(); } } continue; } int curv = e.getCurv() + c[i][j]; int curw = e.getCurw() + w[i][j]; int[] lastroute = e.getRoute(); int[] curroute = new int[110]; for(int k = 0; k <= e.getUnits_i(); k++){ curroute[k] = lastroute[k]; } curroute[i] = j; q.add(new Node(curw, curv, i, curroute)); } } if(ans == Integer.MAX_VALUE){ System.out.println("不能找出总价格不超过 c的最小重量机器的方案"); }else{ System.out.println("满足方案的最小重量是:" + ans); System.out.println("方案是:"); for(int j = 0; j < n; j++){ System.out.print(route[j] + " "); } } } }
2.最大 k 乘积问题: 设 I 是一个 n 位十进制整数。如果将 I 划分为 k 段,则可得到 k 个整数。这 k 个整数的乘积称为 I 的一个 k 乘积。试设计一个算法,对于给定的 I 和 k ,求出 I 的最大 k 乘积。
回溯法设计:
import java.util.Scanner; public class 最大k成绩 { private static long ans; private static Scanner cin; static{ cin = new Scanner(System.in); } static void work(int cur, int i, int k, long v){ //System.out.println("i = " + i + " cur = " + cur + " k = " + k); if(i == k){ ans = Math.max(ans, v); return; } if(cur == 0){ return; } int MOD = 1; while(cur / MOD != 0){ work(cur % MOD, i + 1, k, v * (cur / MOD)); MOD *= 10; } } public static void main(String[] args) { int num, k; System.out.println("请输入数字num和要分成的段数k: "); while(cin.hasNext()){ num = cin.nextInt(); k = cin.nextInt(); ans = Long.MIN_VALUE; work(num, 0, k, 1L); if(ans == Long.MIN_VALUE){ System.out.println("整数" + num + "不能被分成" + k + "段"); }else{ System.out.println(num + "的最大" + k + "乘积是: " + ans); } System.out.println("请输入数字num和要分成的段数k: "); } } }