动态规划->装配线调度

2013年03月1日

    最近在看《编程之美》,上面有一个题目是求字符串的相似度,书上给的解法使用递归,但是通过递归的结果我们可以看到明显有重复的结果。作者提出这个问题,并让我们解决,我网上找到一些说法,可以用动态规划。由于大学的时候把这些东西都忘记了,没办法就重新读《算法导论》第15章《动态规划》。

以下的这几句话是摘自书上的

和分治法一样,动态对话(dynamic programming)是通过组合子问题的解而解决整个问题的。分治算法是指将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的接。与此不同,动态对话是用于子问题不是独立的情况, 也就是各自问题包含公共的子子问题。在这种情况下, 若用分治法则会做许多不必要的工作,即重复求解公共子子问题。动态对话算法对每个子子问题只求解一下,将其结果保存在一张表中,从而可以避免每次遇到各个子问题时重复计算答案;

动态规划通常应用于最优化问题。此类问题可能有多种可行解。每个解有一个值,而我们希望找出一个具有最优(最大或最小)值的接。称这样的解为该问题的“一个”最优解(而不是“确定“的”最优解“),因为可能存在多个取最优值的解。

动态规划算法的设计可以分为如下4个步骤:

1)描述最优解的结构;

2)递归定义最优解的值;

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

4)由计算出的结果构造一个最优解。

第1~3步构成问题的动态规划解的基础。第4步只要求计算最优解的值时可以略去。如果的确做了第4步,则有时要在第3步的计算中记录一些附加信息,是构造一个最优解变得更容易;

书上提到的第一个问题就是

1、装配线调度

动态规划->装配线调度_第1张图片

上图为书上描述的问题:

我在设计程序中,为了和C++中的数组一直,数组下标0表示上图的1,1表示上图的2.....依次类推;

分析:

/*
 * 用动态规划求最快的装配线
 * 有两条装配线(i\k),每条装配线上有j个装配站点,在每个装配站点装配机器所需要的时间为a[i][j],
 * 在装配过程中,可以由一条装配线i的站点j-1移动到另外一条装配线k,所消耗的时间为t[i][j]
 * (表示从装配线i中站点j-1移动到另一条装配线k中站点j所消耗的时间;
 * F[i][j] : 表示到达装配线i上的站点j,装配零件所需要的时间(当然包括a[i][j])
 * a[i][j] : 表示零件在装配线i上的站点j装配零件所需要的时间
 * e[i] : 表示从进入装配线i所需要的时间
 * x[i] : 表示从装配线i离开所需要的时间
 * F[i][j] = e[i] 其中 j = 1;  (递归式)
 *     或者  min(F[i][j-1] + a[i][j], S[k][j-1] + t[i][j] + a[i][j]); 其中j != 1,i\k分别是两条装配线
 * 以上就是递归式
 * 表示:当零件到达装配线i上的站点j装配零件所需要的时间为:
 * 1、当j = 1时,为e[i]
 * 2、当j != 1时,算出零件达到装配线i上的站点j-1装配零件所需要的时间 + 在装配线i上站点j装配零件所需要的时间
 * 算出零件到达装配线k上的站点j-1装配零件所要的时间 + 从装配线k上的站点j-1移动到装配线i的站点j所要的时间 + 在装配线i上站点j装配零件所需要的时间
 * 取两者的最小值
 */

#include <iostream>
using namespace std;

int n = 6;  //站点数

int e[2] = {2, 4};   //进入两个装配线所耗的时间
int x[2] = {3, 2};   //离开两个装配线所耗的时间

int a[2][6] = {{7, 9, 3, 4, 8, 4},    //在装配线i的站点j装配所要的时间
				{8, 5, 6, 4, 5, 7}};

int t[2][6] = {{0, 2, 3, 1, 3, 4},   //在装配线k上站点j-1离开到装配线i的站点j所需要的时间
				{0, 2, 1, 2, 2, 1}};

int F[2][6] = {{0}, {0}};  //在装配线i上的站点j所需要的最少时间

int S[2] = {0};   //在装配线i上完成所需要的最少时间

int l[2][6] = {{0},{0}};  //到达装配线i上的站点j所需最少时间时,其上一站所在的装配线

int exitLine = 0 ;   //当从最后一个装配站点处来的时候,这个装配站点所在的装配线

/*
 * 主要算法
 */
int fastestWay(int F[][6], int a[][6], int t[][6])
{
	F[0][0] = e[0] + a[0][0];
	F[1][0] = e[1] + a[1][0];
	for(int i = 1; i< n; i++)
	{
		//到达装配线0的站点i所需要的最少时间
		if( (F[0][i-1] + a[0][i]) > (F[1][i-1] + t[1][i] + a[0][i]))
		{
			F[0][i] = F[1][i-1] + t[1][i] + a[0][i];
			l[0][i] = 1;   //记录上一个站点i-1所在的线路
		}
		else
		{
			F[0][i] = F[0][i-1] + a[0][i];
			l[0][i] = 0;
		}

		//到达装配线1的站点i所需要的最少时间
		if ((F[1][i-1] + a[1][i]) > (F[0][i-1] + t[0][i] + a[1][i]))
		{
			F[1][i] = F[0][i-1] + t[0][i] + a[1][i];
			l[1][i] = 0;
		}
		else
		{
			F[1][i] = F[1][i-1] + a[1][i];
			l[1][i] = 1;
		}
		  
	}
	S[0] = F[0][5] + x[0];
	S[1] = F[1][5] + x[1];

	//输出达到任意一条装配线上的站点i所需要的最短时间
	for(int i = 0; i<=1; i++)
	{
		for(int j= 0; j<n; j++)
			cout<<F[i][j]<<" ";

		cout<<endl;
	}

	for(int i = 0; i<=1; i++)
	{
		for(int j = 0; j<n; j++)
			cout<<l[i][j] + 1<<" ";
		cout<<endl;
	}

	return min(S[0], S[1]);
	
}

/*
 *打印最快的路线
 */
void print()
{
	if(S[0]> S[1])
	{
		exitLine = 2;
		int line = 2;
		int station = n;
		cout<<"装配线"<<line<<", 站点"<<station<<endl;

		for (int i = n-1 ; i>0; i--)
		{
			line = l[line][i];	
			cout<<"装配线"<<line + 1<<", 站点"<<i<<endl;
		}
	}
	else
	{
		exitLine = 1;
		int line = 1;
		int station = n;
		cout<<"装配线1"<<", 站点"<<n<<endl;
		for(int i = n-1; i>0; i--)
		{
			//上一个站点所在的装配线
			line = l[line][i];
			cout<<"装配线"<<line + 1<<", 站点"<<i<<endl;	
		}

	}
	cout<<endl;
}

/*
 * 递归输出线路
 * line : 装配线
 * station : 站点
 */
void printRecur(int line, int station)
{
	if (station == 0)
	{
		return;
	} 
	else
	{
		int newLine = l[line - 1][station-1];  //求上一个站点的所在线路
		printRecur(newLine + 1, station-1);
		cout<<"装配线"<<line<<", 站点"<<station<<endl;
	}
	
}

void main()
{
	int fastWay = fastestWay(F, a, t);
	cout<<"最快的时间为:"<<fastWay<<endl;

	cout<<"逆序输出路线:"<<endl;
	print();
	cout<<"顺序输出路线:"<<endl;
	printRecur(exitLine, n);
	system("pause");
}
运行结果:


动态规划->装配线调度_第2张图片


你可能感兴趣的:(动态规划->装配线调度)