今天看完了算法导论动态规划的一些简单介绍以及关于装配线调度的系列的问题,真心的感觉算法这玩意好虐,但是真心的有趣,废话不说,切入正题。
书上说的动态规划是分四个步骤进行求解,抄抄:描述最优解结构,递归定义最优解,按照自底向上的规则求解最优解的值,计算出结果并构造出最优解。书上还说的是动态规划实际上和分支法有点相似,这也貌似是为什么我们上课的时候把动态规划安排在分治法之后讲解。但是它们之间的区别就是动态规划进行分解的各个子结构之间是有点关系的,不是像分治法那样是独立的,书上还说一个问题的最优解包含了子问题的一个最优解,这叫做最优子结构是动态规划的标志。
先来描述一下书上的那些个问题,直接从网上copy一份描述,多了去了
找出通过工厂装配线的最快方式的制造问题。共有两条装配线,每条有n个装配站,装配线i的第j个装配站完成工作需要的时间为a[i][j] 。在通过i装配线的第j个装配站后,产品可以直接进入i装配线的j+1个装配站,当中不需要损耗时间;产品也可以进入另外一条装配线的j+1个装配站,这个过程需要损耗t[i][j]时间。产品从初始站进入装配线分别需要损耗时间为e[1],e[2] ,产品从装配线进入终点站分别需要时间为x[1],x[2]。求一个产品从初始站到达终点站的最快路径。
书上规定了一个fi[j]用于表示的是从装配线的始发点到站点Si,j的最快的时间,f*为所有路线中最快的时间。那么我们可以得到下面的几个式子:
1> f*=min(f1[n]+x[1],f2[n]+x[2])//这个是很能理解的吧,最短的路线是要么从始发点到站点S1,n 的时间加上从站点离开装配线花去的时间x[1],,要么是从始发点到站点S2,n 的时间加上从站点离开装配线花去的时间x[2],取他们之间的最短的一个。
2> f1[1]=e[1]+a[1][1];
f2[1]=e[2]+a[2][1];//这个都已理解的
接下来的这个就是递归的重点的了;
3> f1[j]=min(f1[j-1]+a[1][j],f2[j-1]+t[2][j-1]+a[1][j]);//这个的意思就是对于站点S1,j而言的话经过他的最短的时间要么是从同一个装配线的前一个站点来的,要么是从不同装配线的前一个站点来的,只不过在这个过程之中会多了一个离开这个装配线到另一条装配线的时间的消耗而已。
f2[j]=min(f2[j-1]+a[2][j],f1[j-1]+t[1][t-1]+a[2][j]);//这个是上面的那个的而一个对应,应该好理解
下面这个我就看了好久才明白啥意思
4>定义一个li[j],它实际上是一个装配线的标号,在这个问题里面要么是1要么是2,代表的意思是,在这条装配线里面装配站j-1被通过装配站Si,j的最快路线所使用(实际上我到现在都有点晕的就是这个东西除了后来能够输出最短的一条路线外有什么功能或者说意思呢?甚至我都不明白为什么它要设置这么一个东西,请看到这文章的大牛指导下,谢谢,反正就是感觉我自己第一次看这个东西感觉自己是想不到的)l*表示的是经过的站点n被最快的一条路线所使用。
在就是计算时间(这部分我看的是有点糊涂的,请高手指导,对于那个证明那次数那点我没看你明白)
总体分析下:这个问题的最优的解就是求最短的路线,子问题的最优解就是:求到站点Sij的最快路线,各个自问自是相互纠结的,所以用不了分治法。把一个大问题分解成为各个小的相互纠结的问题,可以使用DP,先构造一个最优子结构再说,这个东西我看到了在写。
我的疑问就是:假如是多条装配线呢?这里求解fij是使用的是穷尽的方法计算的,比较各个的多少在决定,假如数量很大的装配线呢?
下面贴上自己写的代码:
#include<iostream>
using namespace std;
int n;
int e1,e2,x1,x2;
int a[3][1000];
int f[3][1000];
int t[3][1000];
int l1[1000],l2[1000];
int ff,l;//f是最终的最快的,l则是相应的那个调度线
void dp()
{
f[1][1]=e1+a[1][1];
f[2][1]=e2+a[2][1];
for(int j=2;j<=n;++j)
{
if(f[1][j-1]<=f[2][j-1]+t[2][j-1])
{
f[1][j]=f[1][j-1]+a[1][j];
l1[j]=1;//这里的意思就是当由f[1][j-1]直接(无开销的这段路)到达f[1][j]时候我们是经过站点j-1,这个站点是在调度线1上面的。是经过调度线1直接到达的,
}
else
{
f[1][j]=f[2][j-1]+t[2][j-1]+a[1][j];
l1[j]=2;//我一开始这里不明白为什么事l1[j]后来看了看li[j]的定义,明白了,这里的i是由最终的那个站点决定的,这里讨论的是调度线1上的站点,S1,j
}
//同理就是其他的调度线2上面的东西了
if(f[2][j-1]<=f[1][j-1]+t[1][j-1])
{
f[2][j]=f[2][j-1]+a[2][j];
l2[j]=2;
}
else
{
f[2][j]=f[1][j-1]+t[1][j-1]+a[2][j];
l2[j]=1;
}
if(f[1][n]+x1<=f[2][n]+x2)
{
ff=f[1][n]+x1;
l=1;
}
else
{
ff=f[2][n]+x2;
l=2;
}
}
}
void print_station()
{
int i=l;
cout<<"line"<<l<<"stastion "<<n<<endl;
for(int j=n;j!=1;--j)
{
if(i==1)
i=l1[j];
else
i=l2[j];//这是借鉴别人的,哈哈
}
cout<<"line"<<i<<"station"<<j-1<<endl;
}
int main()
{
cout << "输入装配站的个数: ";
cin >> n;
cout << "输入进入装配线1,2所需的时间e1, e2 :";
cin >> e1 >> e2;
cout << "输入离开装配线1, 2所需的时间x1, x2: ";
cin >> x1 >> x2;
cout << "输入装配线1上各站加工所需时间a[1][j]: ";
for(int i=1; i<=n; ++i)
cin >> a[1][i];
cout << "输入装配线2上各站加工所需时间a[2][j]: ";
for(int k=1; k<=n; ++k)
cin >> a[2][k];
cout << "输入装配线1上的站到装配线2上的站所需时间t[1][j]: ";
//注意这里是i<n,不是i<=n
for(int m=1; m<n; ++m)
cin >> t[1][m];
cout << "输入装配线2上的站到装配线1上的站所需时间t[2][j]: ";
for(int p=1; p<n; ++p)
cin >> t[2][p];
dp();
cout << "最快需要时间: " << ff << endl;
cout << "路线是: " << endl;
print_station();
cout << endl;
return 0;
}
顺便说下,我自己是第一次的接触算法,第一次的看算法导论,所以算法导论我自己看的有的地方不是太了解,上面的代码大多是自己结合算法导论的伪代码写的,有的细节是借鉴了网络的,反正让自己了解,进步就OK了。哈哈