您的位置:首页 > 其它

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

2012-10-19 13:51 387 查看
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 小结

动态规划问题的关键是寻找最优子结构,问题的一个最优解中包含了子问题的最优解,并分析重叠子问题,其中最关键的是写出上述的递归式,分析问题的递归性,然后才能找到子问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: