6.DynamicProgramming

思想:

最优子结构性质:一个问题的最优解包含了子问题的最优解。

重叠子问题性质:在求解问题的过程中反复求解某些子问题。

按照重叠性,以迭代法自底向上的求解问题。

基本步骤:

1.分析问题结构,是否满足最优子结构性质

2.给出最优解所对应的最优值迭代公式

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

求解问题:

一、矩阵连乘问题:

Input:n个矩阵{A1,A2,...,An},其中AiAi+1是可乘的

Output:n个矩阵的乘积的最优计算次序

1.分析问题是否具有最优子结构

假设A(i,j)表示从矩阵ij的最优计算次序,则A(1,n)即为所求。

计算A(1,n)总可以表示为分别计算子问题A(1,k)A(k+1,n)再将这两个中间结果相乘,

如果A(1,k)A(k+1,n)不是最优计算次序,则总可以被替换,使得A(1,n)更优。故原问题具有最优子结构性质。

2.给出最优解所对应的最优值的迭代公式

C(i,j)表示矩阵ij的连乘的计算量


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

public class MatrixMultProblem {

	public static void main(String[] args) {
		int[] A = { 4, 6, 5, 8, 15 };
		MatrixMultProblem test = new MatrixMultProblem();
		String optOder = test.getTheOptimalCaculatOrder(A);
		System.out.println(optOder);
	}

	/**
	 * 
	 * @param a
	 *            A[i]=第i个矩阵的行数,也即第i-1个矩阵的列数
	 * @return 最优计算次序,以加括号的方式表示:((A1A2)A3)(A4)
	 */
	public String getTheOptimalCaculatOrder(int[] a) {
		int n = a.length - 1;// 表示矩阵个数
		int[][] c = new int[n][n];// C[i][j]表示第i到j个矩阵连乘的最优花费
		int[][] p = new int[n][n];// P[i][j]表示第i到j个矩阵连乘的中间矩阵
		// 初始化,C[i][i]=0
		for (int i = 0; i < c.length; i++) {
			c[i][i] = 0;
		}
		for (int d = 2; d <= n; d++) {// d表示连乘的矩阵的个数
			for (int i = 0; i <= n - d; i++) {// 起始矩阵
				int j = i + d - 1;// 结束矩阵
				int min = c[i][i] + c[i + 1][j] + a[i] * a[i + 1] * a[j + 1];
				p[i][j] = i;
				for (int k = i + 2; k <= j; k++) {// 计算最优的中间矩阵,将矩阵分开
					int temp = c[i][k - 1] + c[k][j] + a[i] * a[k] * a[j + 1];
					if (temp < min) {
						min = temp;
						p[i][j] = k - 1;
					}
				}
				c[i][j] = min;
			}
		}
		System.out.println("最优值=" + c[0][n - 1]);
		return getOder(p, 0, n - 1);
	}

	/**
	 * 由最优值构造最优解
	 * 
	 * @param p
	 *            p[i][j]表示第i到j个矩阵连乘的中间矩阵
	 * @param l
	 *            表示起始矩阵
	 * @param h
	 *            表示结束矩阵
	 * @param res
	 *            存储最优计算次序方案
	 */
	private String getOder(int[][] p, int l, int h) {
		// TODO Auto-generated method stub
		String res = "";
		if (l >= h) {
			res += ("A" + (l + 1));
			return res;
		}
		int t = p[l][h];
		if (l != t)
			res += ("(");
		res += getOder(p, l, t);
		if (l != t)
			res += (")");
		if (h != t + 1)
			res += ("(");
		res += getOder(p, t + 1, h);
		if (h != t + 1)
			res += (")");
		return res;
	}

	private void disp(int[][] a) {
		// TODO Auto-generated method stub
		for (int i = 0; i < a.length; i++) {
			for (int j = 0; j < a[0].length; j++) {
				System.out.print(a[i][j] + "\t");
			}
			System.out.println();
		}
	}
}


二、最优二叉查找树的构造:

二叉查找树:中序遍历为有序。最优二叉查找树:按照元素出现的概率,构造二叉查找树使得平均比较次数最少。

Input:有序的键值,及各键值查找的概率

Output:由所有键值构成的平均查找次数最少的二叉查找树

1.分析问题是否具有最优子结构

假设a1,a2,...,an为从小到大排好序的键值,对应的查找概率为p1,p2,...,pnTij为键ai,...,aj构成的最优二叉查找树,对应的平均比较次数为C(i,j)。则T1n为所要求的最优解,C(1,n)为所要求的最优值。若T1n的中间根节点为ak,则T1k-1Tk+1n分别为其左右子树,若T1k-1Tk+1n不是最优二叉查找树,则一定存在比T1n更优的二叉查找树,故根节点ak的左右子树一定也是最优二叉查找树。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

class BTree {
	char key;
	BTree lchild;
	BTree rchild;
}

public class PlainOptBinarySearchTree {

	public static void main(String[] args) {
		char[] key = { 'A', 'B', 'C', 'D' };
		double[] p = { 0.2, 0.3, 0.1, 0.4 };
		PlainOptBinarySearchTree obst = new PlainOptBinarySearchTree();
		BTree t = obst.plaining(key, p);
		obst.disp(t);
	}

	// 中序遍历二叉树t
	private void disp(BTree t) {
		// TODO Auto-generated method stub
		if (t != null) {
			disp(t.lchild);
			System.out.print(t.key + " ");
			disp(t.rchild);
		}
	}

	/**
	 * 构造最优二叉查找树方法
	 * 
	 * @param key
	 *            关键字
	 * @param p
	 *            各关键字出现的概率
	 * @return 返回构建的二叉树
	 */
	public BTree plaining(char[] key, double[] p) {
		double[][] C = new double[p.length][p.length];
		int[][] root = new int[p.length][p.length];
		// 初始化
		for (int i = 0; i < p.length; i++) {
			C[i][i] = p[i];
			root[i][i] = i;
		}
		// 迭代法得到最优值
		for (int n = 2; n <= p.length; n++) {// 子树节点数n
			for (int i = 0; i <= p.length - n; i++) {// 子树第一个节点编号
				int j = i + n - 1;// 子树最后一个节点编号
				double sum = 0;
				for (int k = i; k <= j; k++) {
					sum += p[k];
				}
				double min = 0 + C[i + 1][j];
				root[i][j] = i;
				for (int k = i + 1; k < j; k++) {
					double temp = C[i][k - 1] + C[k + 1][j];
					if (temp < min) {
						min = temp;
						root[i][j] = k;
					}
				}
				double temp = C[i][j - 1];
				if (temp < min) {
					min = temp;
					root[i][j] = j;
				}
				C[i][j] = min + sum;
			}
		}
		return getBTree(key, root, 0, p.length - 1);
	}

	/**
	 * 递归法生成树
	 * 
	 * @param key
	 *            关键字
	 * @param root
	 *            root[i][j]表示i到j这些节点的父节点
	 * @param i
	 * @param j
	 * @return 返回由root构造的二叉树
	 */
	public BTree getBTree(char[] key, int[][] root, int i, int j) {
		if (i <= j) {
			int r = root[i][j];
			BTree t = new BTree();
			t.key = key[r];
			t.lchild = getBTree(key, root, i, r - 1);
			t.rchild = getBTree(key, root, r + 1, j);
			return t;
		}
		return null;
	}
<p>}</p>

三、近似串匹配问题:----还没搞懂

Input:样本串P=p1p2...pm,文本串T=t1t2...tn

Output:所有位置1<=i<=m-n+1,使得titi+1....ti+n-1p1p2...pn至多有k个错配。也叫样本P在文本T中的K近似匹配。

1.分析问题是否具有最优子结构

假设D(i,j)表示样本串P的前i个字符与文本串T的前j个字符的最多差别个数,则D(m,n)即为所求。(似乎不一定满足最优子结构)

2.给出最优解所对应的最优值的迭代公式

对于样本串P的第i个元素pi和文本串T的第j个元素tj,存在两种情况pi=tjpi!=tj

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

四、多段图的最短路径问题:

Input:多段图的段数k,顶点数n,每条边的权重cij

Output:源点到汇点的最短路径及决策过程

1.分析问题是否具有最优子结构

从起点到终点的最短路径包含了该路径上各点到终点的最短路径。

假设C(i)为定点i到汇点n的最短路径长度,则C(1)即为所求的最优值。若C(1)不具最优子结构性质,则子路径C(i)不为最优,此时一定存在比C(1)更优值。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

public class M_GraphShortestProblem {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int k = 5;
		int n = 10;
		int[][] c = new int[10][10];
		for (int i = 0; i < c.length; i++) {
			for (int j = 0; j < c.length; j++) {
				c[i][j] = 10000;
			}
		}
		c[0][1] = 4;
		c[0][2] = 3;
		c[0][3] = 3;
		c[1][4] = 2;
		c[1][5] = 3;
		c[2][4] = 2;
		c[2][5] = 1;
		c[2][6] = 3;
		c[3][5] = 2;
		c[3][6] = 1;
		c[4][7] = 3;
		c[4][8] = 4;
		c[5][7] = 2;
		c[5][8] = 1;
		c[6][7] = 6;
		c[6][8] = 4;
		c[7][9] = 5;
		c[8][9] = 3;
		new M_GraphShortestProblem().getTheShortestPath(c, k, n);

	}

	/**
	 * 
	 * @param c
	 *            c[i][j]表示顶点i到j的距离
	 * @param k
	 *            k表示段的个数
	 * @param n
	 *            n表示顶点个数
	 */
	public void getTheShortestPath(int[][] c, int k, int n) {
		int[] pre = new int[n];
		int[] C = new int[n];
		// 初始化
		C[n - 1] = 0;
		int temp = 0;
		for (int i = n - 2; i >= 0; i--) {
			C[i] = c[i][i + 1] + C[i + 1];
			pre[i] = i + 1;
			for (int j = i + 2; j <= n - 1; j++) {
				temp = c[i][j] + C[j];
				if (temp < C[i]) {
					C[i] = temp;
					pre[i] = j;
				}
			}
		}
		System.out.println("最短路径长度为:" + C[0]);
		System.out.print("0->");
		dispPath(pre, 0, n - 1);
	}

	// 递归得到最短路径
	private void dispPath(int[] pre, int k, int n) {
		// TODO Auto-generated method stub
		if (k >= n - 1) {
			System.out.print(pre[k]);
			return;
		}
		System.out.print(pre[k] + "->");
		dispPath(pre, pre[k], n);
	}
}

五、多源最短路径问题Floyd算法:

Input:邻接矩阵w

Output:每个顶点到其他所有顶点的最短路径

1.分析问题是否具有最优子结构

从起点到终点的最短路径包含了该路径上各点到终点的最短路径。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

public class MultiSourseShortestRoutProblem {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[][] w = { { 0, 10000, 5, 6 }, { 4, 0, 10000, 10000 },
				{ 10000, 5, 0, 3 }, { 10000, 2, 10000, 0 } };
		new MultiSourseShortestRoutProblem().floyd(w);
	}

	/**
	 * @param w
	 *            存储各边权重
	 */
	public void floyd(int[][] w) {
		int[][] d1 = new int[w.length][w.length];// 存放上次得到的最短路径
		int[][] d2 = new int[w.length][w.length];// 存放本次计算的最短路径
		int[][] path = new int[w.length][w.length];// path[i][j]保存点i到点j的最短路径的最后一个中间节点
		// 初始化
		for (int i = 0; i < d1.length; i++) {
			for (int j = 0; j < d1[0].length; j++) {
				d1[i][j] = w[i][j];
				path[i][j] = -1;
			}
		}

		// 动态规划,逐级构造,每次添加一个中间节点
		for (int k = 0; k < w.length; k++) {// 一次添加一个中间节点,更新最短路径矩阵
			for (int i = 0; i < w.length; i++) {
				for (int j = 0; j < w.length; j++) {
					d2[i][j] = d1[i][j];
					int temp = d1[i][k] + d1[k][j];
					if (temp < d1[i][j]) {
						d2[i][j] = temp;
						path[i][j] = k;
					}
				}
			}
			d1 = d2.clone();
		}
		System.out.println("所有顶点之间的最短路径长度:");
		disp(d2);
		System.out.println("路径信息矩阵:");
		disp(path);
	}

	/**
	 * 输出二维矩阵a
	 * 
	 * @param a
	 */
	private void disp(int[][] a) {
		// TODO Auto-generated method stub
		for (int i = 0; i < a.length; i++) {
			for (int j = 0; j < a[0].length; j++) {
				System.out.print(a[i][j] + "\t");
			}
			System.out.println();
		}
	}
}

六、0\1背包问题:

Inputn个重量为w1,w2,...,wn的物品,价值分别为v1,v2,...,vn;最大载重量为W的背包。

Output:求背包能装下的最大价值量及相应物品。

1.分析问题是否具有最优子结构

不同的问题划分方式会得到不同的问题子结构。

假设由前i个物品组成且背包的总载重为j时的最优解为V[i,j],则原问题的最优解为V[n,W]。如V[n,W]包含物品n,则余下的物品组成的子问题(由前n-1个物品组成,且背包的载重量为W-wn)也必然为最优。

2.给出最优解所对应的最优值的迭代公式

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

public class BeiBaoProblem {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// int W = 9;
		// int n = 4;
		// int[] w = { 2, 3, 4, 5 };
		// int[] v = { 3, 4, 5, 7 };
		 int W = 10;
		 int n = 4;
		 int[] w = { 4, 7, 5, 3 };
		 int[] v = { 40, 42, 25, 12 };
		new BeiBaoProblem().getTheMaxValue(n, w, v, W);
	}

	/**
	 * 
	 * @param n
	 *            物品数量
	 * @param w
	 *            物品重量
	 * @param v
	 *            物品价值量
	 * @param W
	 *            背包载重量
	 */
	public void getTheMaxValue(int n, int[] w, int[] v, int W) {
		int[][] maxV = new int[n + 1][W + 1];// maxV[i][j]表示i个货物且背包载重为j时能装的最大价值
		int[] putW = new int[n + 1];// 记录第i件物品是否被装入
		// 初始化
		for (int i = 0; i <= n; i++) {
			maxV[i][0] = 0;
		}
		for (int i = 0; i <= W; i++) {
			maxV[0][i] = 0;
		}
		for (int i = 1; i <= n; i++) {// 一次添加一件物品
			for (int j = 1; j <= W; j++) {// 载重量逐渐增加
				if (w[i - 1] <= j) {// 背包能装下i物品
					maxV[i][j] = maxV[i - 1][j];
					int temp = maxV[i - 1][j - w[i - 1]] + v[i - 1];
					if (temp > maxV[i][j]) {
						maxV[i][j] = temp;
						putW[i] = 1;
					}
				} else {
					maxV[i][j] = maxV[i - 1][j];
				}
			}
		}
		System.out.println("最大值为:" + maxV[n][W]);
		showTheW(w, maxV, n, W);
	}

	// 如果maxV[i][W]!=maxV[i-1][W],说明第i个物品被加入
	private void showTheW(int[] w, int[][] maxV, int n, int W) {
		// TODO Auto-generated method stub
		System.out.println("背包中的物品有:");
		int t = W;
		for (int i = n; i > 0; i--) {
			if (maxV[i][t] != maxV[i - 1][t]) {
				t = t - w[i - 1];
				System.out.print(i + "\t");
			}
		}
	}
}

七、最长公共子序列问题:

Input:两个字符串str1str2

Ooutput:两个字符串的最长公共子序列

1.分析问题是否具有最优子结构

假设L(i,j)表示字符串str1的前i个字符和str2的前j个字符的最大公共子串长度,则L(m,n)就表示两个字符串的最长公共子序列长度,即为最优值。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class TheLongestCommonSubsequence {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// char[] str1 = { 'a', 'c', 'r', 'd', 'k', 'f', 'a' };
		// char[] str2 = { 'd', 'c', 'e', 'd', 'l', 'f', 'a' };
		char[] str1 = { 'a', 'b', 'a', 'b', 'a', 'b', 'c', 'd', 'e' };
		char[] str2 = { 'a', 'b', 'b', 'b', 'b', 'b' };
		int m = 9, n = 6;
		new TheLongestCommonSubsequence().getTheLongestComSubseq(str1, str2, m,
				n);
	}

	/**
	 * 
	 * @param str1
	 *            字符串str1长度m
	 * @param str2
	 *            字符串str2长度n
	 * @param m
	 * @param n
	 */
	public void getTheLongestComSubseq(char[] str1, char[] str2, int m, int n) {
		int[][] L = new int[m + 1][n + 1];
		int[][] last = new int[m + 1][n + 1];
		// 初始化
		for (int i = 0; i <= m; i++) {
			L[i][0] = 0;
		}
		for (int i = 0; i <= n; i++) {
			L[0][i] = 0;
		}
		for (int i = 1; i <= m; i++) {
			for (int j = 1; j <= n; j++) {
				if (str1[i - 1] == str2[j - 1]) {
					L[i][j] = L[i - 1][j - 1] + 1;
					last[i][j] = 1;// 表示str1的第i个字符和str2的第j个字符相等
				} else if (L[i - 1][j] > L[i][j - 1]) {
					L[i][j] = L[i - 1][j];
					last[i][j] = 2;// 表示str1的第i个字符不在最长公共串里
				} else if (L[i - 1][j] == L[i][j - 1]) {
					L[i][j] = L[i][j - 1];
					last[i][j] = 3;// 表示str2的第j个字符不在最长公共串里且str1的第i个字符不在最长公共串里
				} else {
					L[i][j] = L[i][j - 1];
					last[i][j] = 4;// 表示str2的第j个字符不在最长公共串里
				}
			}
		}
		System.out.println(String.valueOf(str1) + " 和 " + String.valueOf(str2)
				+ "的最大公共子串长度为:" + L[m][n]);
		Set<String> res = new HashSet<String>();
		getStr(str1, last, m, n, "", res);
		System.out.println("最大公共子串为:" + res.toString());
	}

	private void getStr(char[] str1, int[][] last, int m, int n, String s,
			Set<String> res) {
		// TODO Auto-generated method stub
		if (m > 0 && n > 0) {
			if (last[m][n] == 1) {
				s = str1[m - 1] + s;
				getStr(str1, last, m - 1, n - 1, s, res);
			} else if (last[m][n] == 2) {// 表示str1的该字符不在最长公共串里
				getStr(str1, last, m - 1, n, s, res);
			} else if (last[m][n] == 3) {// 表示str2的该字符不在最长公共串里且str1的第i个字符不在最长公共串里
				getStr(str1, last, m, n - 1, s, res);
				getStr(str1, last, m - 1, n, s, res);
			} else if (last[m][n] == 4) {// 表示str2的该字符不在最长公共串里
				getStr(str1, last, m, n - 1, s, res);
			}
		} else {
			res.add(s);
		}
	}
}

八、最长的单调递增子序列

Input:一个长度为n的整数序列A

Output:该序列A中最长的单调递增子序列

1.分析问题是否具有最优子结构

假设L(i,j)表示序列A中前i个元素构成的小于j的单调递增子序列长度,则L中的最大值即为所求的最优值。若最长单调递增子序列为B长度为k,且B(k)A中第i个元素,则L(i,B(k))为最优值,若B(k-1)A中第j个元素,则L(j,B(k-1))也一定是子问题(序列A的前j个元素的单调递增子序列)的最优值。

2.给出最优解所对应的最优值的迭代公式


3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

public class TheLongestIncreaseSubsuquence {

	public static void main(String[] args) {
		int[] A = { 1, 2, 5, 3, 9, 4, 5, 6 };
		new TheLongestIncreaseSubsuquence().LIS(A);
	}

	/**
	 * 求序列A的最长递增子序列
	 * 
	 * @param A
	 */
	public void LIS(int[] A) {
		int[] L = new int[A.length];
		int[] p = new int[A.length];
		// 初始化
		L[0] = 1;
		for (int i = 0; i < A.length; i++) {
			p[i] = -1;
		}
		// 自底向上方法,求的最优值
		for (int i = 1; i < A.length; i++) {
			L[i] = 1;
			for (int k = 0; k < i; k++) {
				if (A[k] < A[i]) {
					if (L[i] < L[k] + 1) {
						L[i] = L[k] + 1;
						p[i] = k;
					}
				} else if (A[k] == A[i]) {
					if (L[i] < L[k]) {
						L[i] = L[k];
						p[i] = k;
					}
				}
			}
		}
		// 构造最优解
		int ll = 1;
		int li = 0;
		for (int i = 0; i < A.length; i++) {
			if (ll < L[i]) {
				ll = L[i];
				li = i;
			}
			System.out.print(A[i]);
		}
		System.out.println("的最长递增子序列长度为:" + ll);
		System.out.println("最长递增子序列为:");
		dispSubSequence(A, p, li);
	}

	private void dispSubSequence(int[] A, int[] p, int li) {
		// TODO Auto-generated method stub
		if (li > -1) {
			dispSubSequence(A, p, p[li]);
			System.out.print(A[li]);
		}
	}
}

九、最大子段和

Input:一个长度为n的整数序列A

Output:该序列A的最大子段和及该最大子段序列

1.分析问题是否具有最优子结构

假设V(i)表示序列A的前i个元素的最大字段和且包含第i个元素,则V中的最大值即为所求。如果把状态设为这样,则问题是具有最优子结构的。

2.给出最优解所对应的最优值的迭代公式

3.迭代法,自底向上的计算最优值

4.根据最优值反向推导出最优解

代码如下:

package DynamicProgramming;

public class TheBiggestSubSum {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] A = { -3, 2, -1, 4, -5, 2, 1, 2, 3, -4, 5, -6, 7, -8 };
		new TheBiggestSubSum().theBiggestSubSum(A);
	}

	public void theBiggestSubSum(int[] A) {
		int[] V = new int[A.length];
		int[] p = new int[A.length];
		// 初始化
		V[0] = A[0];
		// 构造数组V
		for (int i = 1; i < A.length; i++) {
			if (V[i - 1] <= 0) {
				V[i] = A[i];
				p[i] = i;
			} else {
				V[i] = V[i - 1] + A[i];
				p[i] = p[i - 1];
			}
		}
		// 找到最大值
		int maxSum = 0;
		int maxi = 0;
		for (int i = 0; i < A.length; i++) {
			if (maxSum < V[i]) {
				maxSum = V[i];
				maxi = i;
			}
		}
		System.out.print("序列");
		for (int i = 0; i < A.length; i++) {
			System.out.print(A[i] + ",");
		}
		System.out.println("的最大子段和为:" + maxSum);
		System.out.print("最大子段为:");
		for (int i = p[maxi]; i <= maxi; i++) {
			System.out.print(A[i] + ",");
		}
	}
}


 // keshe.cpp #include “keshe.h” #include #include #include #include #include #include #include using namespace std; // 预处理作业顺序 vector preprocess(const vector<vector>& jobs) { vector order(jobs.size()); for (int i = 0; i < jobs.size(); ++i) { order[i].first = jobs[i][0]; order[i].second = i; } sort(order.begin(), order.end(), [](const IntPair& a, const IntPair& b) { return a.first > b.first; }); return order; } // 动态规划解决3台机器流水作业 JobOrderResult dynamicProgramming(const vector<vector>& jobs) { int n = jobs.size(); int allJobs = (1 << n) - 1; int INF = INT_MAX / 2; vector<vector<int>> dp(1 << n, vector<int>(500, INF)); vector<vector<State>> prev(1 << n, vector<State>(500, {-1, -1})); dp[0][0] = 0; for (int S = 0; S < (1 << n); ++S) { for (int m2 = 0; m2 < 500; ++m2) { if (dp[S][m2] == INF) continue; int m1 = 0; for (int i = 0; i < n; ++i) { if (S & (1 << i)) { m1 += jobs[i][0]; } } for (int k = 0; k < n; ++k) { if (S & (1 << k)) continue; int newS = S | (1 << k); int newM1 = m1 + jobs[k][0]; int newM2 = max(m2, newM1) + jobs[k][1]; int newM3 = max(dp[S][m2], newM2) + jobs[k][2]; if (newM2 < 500 && newM3 < dp[newS][newM2]) { dp[newS][newM2] = newM3; prev[newS][newM2] = {S, m2}; } } } } int minTime = INF; int bestM2 = -1; for (int m2 = 0; m2 < 500; ++m2) { if (dp[allJobs][m2] < minTime) { minTime = dp[allJobs][m2]; bestM2 = m2; } } vector<int> jobOrder; int currentS = allJobs; int currentM2 = bestM2; while (currentS != 0) { int prevS = prev[currentS][currentM2].S; int prevM2 = prev[currentS][currentM2].m2; int addedJob = -1; for (int i = 0; i < n; ++i) { if ((currentS & (1 << i)) && !(prevS & (1 << i))) { addedJob = i; break; } } jobOrder.push_back(addedJob); currentS = prevS; currentM2 = prevM2; } reverse(jobOrder.begin(), jobOrder.end()); return {jobOrder, minTime}; } // 处理用户自定义输入 void handleCustomInput() { int n; cout << "请输入产品数量 (n >= 10): "; while (true) { cin >> n; if (cin.fail() || n < 10) { cin.clear(); while (cin.get() != '\n'); cout << "错误: 产品数量必须不小于10,请重新输入: "; } else { while (cin.get() != '\n'); break; } } vector<int> process1(n); vector<int> process2(n); vector<int> process3(n); cout << "请输入工序1的 " << n << " 个加工时间,用空格分隔: "; for (int i = 0; i < n; ++i) { while (!(cin >> process1[i]) || process1[i] <= 0) { cout << "错误: 加工时间必须为正数,请重新输入该工序的加工时间: "; cin.clear(); while (cin.get() != '\n'); i = 0; } } cout << "请输入工序2的 " << n << " 个加工时间,用空格分隔: "; for (int i = 0; i < n; ++i) { while (!(cin >> process2[i]) || process2[i] <= 0) { cout << "错误: 加工时间必须为正数,请重新输入该工序的加工时间: "; cin.clear(); while (cin.get() != '\n'); i = 0; } } cout << "请输入工序3的 " << n << " 个加工时间,用空格分隔: "; for (int i = 0; i < n; ++i) { while (!(cin >> process3[i]) || process3[i] <= 0) { cout << "错误: 加工时间必须为正数,请重新输入该工序的加工时间: "; cin.clear(); while (cin.get() != '\n'); i = 0; } } vector<vector<int>> data(n, vector<int>(3)); for (int i = 0; i < n; ++i) { data[i][0] = process1[i]; data[i][1] = process2[i]; data[i][2] = process3[i]; } auto processedOrder = preprocess(data); vector<vector<int>> processedData(n); vector<int> originalIndices(n); for (int j = 0; j < n; ++j) { processedData[j] = data[processedOrder[j].second]; originalIndices[j] = processedOrder[j].second; } auto start = chrono::high_resolution_clock::now(); auto result = dynamicProgramming(processedData); auto end = chrono::high_resolution_clock::now(); double dur = chrono::duration_cast<chrono::milliseconds>(end - start).count(); vector<int> originalJobOrder; for (int idx : result.order) { originalJobOrder.push_back(originalIndices[idx] + 1); } cout << "最优加工顺序: "; for (int id : originalJobOrder) cout << id << " "; cout << "\n最小总加工时间: " << result.minTime << " 单位时间" << endl; cout << "运行时间: " << dur << " 毫秒" << endl; } // 进行固定数据的性能测试 void handlePerformanceTest() { cout << “进行固定数据的性能测试:” << endl; vector<vector<vector<int>>> testDatasets = { // 第一组:n=3 { {5, 7, 6}, {7, 4, 9}, {6, 5, 8} }, // 第二组:n=10 { {7, 10, 8, 12, 6, 9, 11, 5, 13, 7}, {9, 8, 11, 7, 10, 12, 6, 13, 5, 9}, {8, 12, 9, 10, 7, 5, 11, 8, 13, 6} }, // 第三组:n=20 { {5, 7, 9, 11, 6, 8, 10, 12, 7, 9, 11, 5, 13, 8, 7, 10, 9, 11, 6, 8}, {8, 6, 10, 7, 9, 11, 5, 12, 8, 10, 12, 6, 13, 5, 9, 8, 11, 7, 10, 12}, {7, 11, 8, 9, 6, 10, 12, 5, 9, 11, 8, 10, 12, 6, 13, 5, 9, 8, 11, 7} } }; for (size_t i = 0; i < testDatasets.size(); ++i) { auto& data = testDatasets[i]; int n = data[0].size(); cout << "\n第" << i + 1 << "组数据 (n=" << n << "):" << endl; vector<vector<int>> transposedData(n, vector<int>(3)); for (int j = 0; j < n; ++j) { for (int k = 0; k < 3; ++k) { transposedData[j][k] = data[k][j]; } } auto processedOrder = preprocess(transposedData); vector<vector<int>> processedData(n); vector<int> originalIndices(n); for (int j = 0; j < n; ++j) { processedData[j] = transposedData[processedOrder[j].second]; originalIndices[j] = processedOrder[j].second; } auto start = chrono::high_resolution_clock::now(); auto result = dynamicProgramming(processedData); auto end = chrono::high_resolution_clock::now(); double dur = chrono::duration_cast<chrono::milliseconds>(end - start).count(); vector<int> originalJobOrder; for (int idx : result.order) { originalJobOrder.push_back(originalIndices[idx] + 1); } cout << "最优加工顺序: "; for (int id : originalJobOrder) cout << id << " "; cout << "\n最小总加工时间: " << result.minTime << " 单位时间" << endl; cout << "运行时间: " << dur << " 毫秒" << endl; } } // keshe.h #ifndef KESHE_H #define KESHE_H #include using namespace std; struct IntPair { int first; int second; }; struct JobOrderResult { vector order; int minTime; }; struct State { int S; int m2; }; // 预处理作业顺序 vector preprocess(const vector<vector>& jobs); // 动态规划求解3台机器流水作业 JobOrderResult dynamicProgramming(const vector<vector>& jobs); // 处理用户自定义输入 void handleCustomInput(); // 进行固定数据的性能测试 void handlePerformanceTest(); #endif // main.cpp #include #include “keshe.h” using namespace std; int main() { cout << “动态规划求解3台机器流水作业” << endl; int choice; do { cout << "\n===== 流水作业调度系统 =====" << endl; cout << "1. 自定义输入一组数据" << endl; cout << "2. 进行固定数据的性能测试" << endl; cout << "3. 退出" << endl; cout << "请选择: "; while (!(cin >> choice) || choice < 1 || choice > 3) { cout << "无效选择,请输入1-3之间的数字: "; cin.clear(); while (cin.get() != '\n'); } while (cin.get() != '\n'); switch (choice) { case 1: handleCustomInput(); break; case 2: handlePerformanceTest(); break; case 3: cout << "退出系统..." << endl; break; } } while (choice != 3); return 0; }现在代码不能实现 // 第三组:n=20 { {5, 7, 9, 11, 6, 8, 10, 12, 7, 9, 11, 5, 13, 8, 7, 10, 9, 11, 6, 8}, {8, 6, 10, 7, 9, 11, 5, 12, 8, 10, 12, 6, 13, 5, 9, 8, 11, 7, 10, 12}, {7, 11, 8, 9, 6, 10, 12, 5, 9, 11, 8, 10, 12, 6, 13, 5, 9, 8, 11, 7} },这是我一开始给你的
06-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值