动态规划:装配线与LCS问题

来源:互联网 发布:数据库预留字段命名 编辑:程序博客网 时间:2024/06/03 18:58

1 概述

1、分治法和动态规划都是通过组合子问题的解来解决整个问题。

2、动态规划适用于各个子问题包含公共子问题的情况。

3、分治法将问题划分为一些独立的子问题。


2 动态规划算法设计思路

1、描述最优子结构

2、递归定义最优解的值

3、按自底向上的方式计算最优解的值


3 装配线调度问题

3.1 问题的描述

有如图的一条装配工厂,有两条并行的装配线,每个站点的处理时间是a(i,j),其中i=1,2,j=1...n。两个装配线间切换的时间为t(i,j)。

输出:在装配线上选择一条最快路径。

3.2 方法一、强力法

图中有两条并行的装配线,经过2^n种尝试,总可以找出最优解,但是时间复杂度很高。

3.3 方法二、动态规划

考虑其中的任意一个装配站a(i,j),离开装配站a(i,j)的时间可以表示为:(如i=1)


若问题是计算一个点的最短时间,这是一个递归的过程,具有O(n)的时间复杂度。但是如果需要知道最快路径,则如果从最后一个装配站开始向前递归,每一个节点j又必须向前递归j-1次,其中的子问题的解进行了多次的重复计算,因此复杂度认为O(2^n)。

由此引出了动态规划,将公共子问题(即离开某站a(i,j)的最短时间和上一节点值),则只需通过简单递归就可组合出最优解。

int _tmain(int argc, _TCHAR* argv[]){int a[2][6] = {7, 9, 3, 4, 8, 4, 8, 5, 6, 4, 5, 7};int t[2][5] = {2, 3, 1, 3, 4, 2, 1, 2, 2, 1};int e1 = 2, e2 = 4, x1 = 3, x2 = 2;vector<LineNode> S1(6), S2(6);LineNode end; //构造子问题的解for (int i = 0; i < 6; i++){if (i == 0){S1[i].Cost = e1 + a[0][0];S2[i].Cost = e2 + a[1][0];}else{int temp1 = (S1[i-1].Cost + a[0][i]), temp2 = (S2[i-1].Cost + t[1][i-1] + a[0][i]); if (temp1 < temp2){S1[i].Cost = temp1; S1[i].preNode = 1;}else{S1[i].Cost = temp2; S1[i].preNode = 2;}int temp3 = (S2[i-1].Cost + a[1][i]), temp4 = (S1[i-1].Cost + t[0][i-1] + a[1][i]); if (temp3 < temp4){S2[i].Cost = temp3; S2[i].preNode = 2;}else{S2[i].Cost = temp4; S2[i].preNode = 1;}}}for (int i = 0; i < 6; i++)cout << S1[i].Cost << " ";cout << endl;for (int i = 0; i < 6; i++)cout << S2[i].Cost << " ";cout << endl;int temp1 = (S1[5].Cost + x1), temp2 = (S2[5].Cost + x2);if (temp1 < temp2){end.Cost = temp1; end.preNode = 1;}else{end.Cost = temp2; end.preNode = 2;}int flag = end.preNode;//输出最短路径for (int i = 5; i>= 0; i--){if (flag == 1){cout << "S1, Node " << i << endl;flag = S1[i].preNode;}else{cout << "S2, Node " << i << endl;flag = S2[i].preNode;}}system("pause");return 0;}


4 最长公共子序列

4.1 问题的描述

给定两个序列X={x1, x2,..., xm}和Y={y1, y2,.., yn},找出两个序列的最长公共子序列。

4.2 问题的分析

首先定义序列的前缀Xi和Yi。

接着定义最优子结构:设两个序列X={x1, x2,..., xm}和Y={y1, y2,.., yn},并设Z={z1, z2,..., zk}为X和Y的任意一个LCS

1)如果xm=yn,则zk=xm=yn,而且Zk-1是Xm-1和Yn-1的一个LCS

2)如果xm^=yn,且zk^=xm,而且Z是Xm-1和Yn的一个LCS

3)如果xm^=yn,且zk^=yn,而且Z是Xm和Yn-1的一个LCS

容易看出重叠子问题的性质,易得递归式


整个问题的最优解就是X序列的某个前缀和Y序列的某个前缀的LCS,因此容易得到动态规划的解

int n = 7, m = 6;char X[] = {'A', 'B', 'C', 'B', 'D', 'A', 'B'};char Y[] = {'B', 'D', 'C', 'A', 'B', 'A'};vector< vector<ListNode2> > cost(n + 1, m + 1);void LCS_Print(int i, int j);void LCS(){for (int i = 1; i < n+1; i++){for (int j = 1; j < m+1; j++){if (X[i-1] == Y[j - 1]){cost[i][j]._length = cost[i - 1][j - 1]._length + 1;cost[i][j]._direction = UPLEFT;}else if (cost[i][j - 1]._length > cost[i - 1][j]._length){cost[i][j]._length = cost[i][j - 1]._length;cost[i][j]._direction = LEFT;}else {cost[i][j]._length = cost[i - 1][j]._length;cost[i][j]._direction = UP;}}}for (int i = 0; i < 8; i++){for (int j = 0; j < 7; j++)cout << "(" << cost[i][j]._length << ", " << cost[i][j]._direction << ") ";cout << endl;}LCS_Print(7, 6);}void LCS_Print(int i, int j){if (i == 0 || j == 0)return;switch (cost[i][j]._direction){case UPLEFT:LCS_Print(i - 1, j - 1);cout << X[i - 1] << " ";return; case LEFT:return LCS_Print(i, j - 1);case UP:return LCS_Print(i - 1, j);default:break;}}


5 小结

动态规划问题的关键是寻找最优子结构,问题的一个最优解中包含了子问题的最优解,并分析重叠子问题,其中最关键的是写出上述的递归式,分析问题的递归性,然后才能找到子问题。