【算法导论】动态规划算法之装配线调度

        和分治算法一样,动态规划是通过组合子问题的解而解决整个问题的。但是与分治算法不同的是,动态规划算法适用于子问题不是独立的情况,也就是各子问题包含公共的子子问题。动态规划通常用于最优化问题的求解。看一个问题是否适合采用动态规划算法,主要有两个标志:最优子结构重叠子问题。

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

重叠子问题:当一个递归算法不断地调用同一问题时,我们说该最优子问题包含重叠子问题。

动态规划算法的设计步骤如下:

1.描述最优解的结构。

2.递归定义最优解的值。

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

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

        下面利用动态规划算法求解一些最优化问题,本文解决装配线调度问题,问题如下:

假设有2条生产线,每条生产线有6个装配点。两条生产线对应点的功能相同,但是时间有所差别。产品需要经过这6个点才能完成生产。在同一条生产线上,产品从一个装配点转到下一个装配点的时间可以忽略,但是从一条生产线到另一条生产线则需要时间消耗。我们的问题是如何找到最短时间的路径。

      上面问题的一般模型如下图:


我们将上述一般模型实例化,可以转换为下图:

        

在上图中,f1[i]为到达第一条生产线中第i个装配站的时间(包括在第i站的时间);f2[i]为到达第二条生产线中第i个装配站的时间(包括在第i站的时间);l1[i]为到达第一条生产线中第i个装配站的上一站是哪条生产线;l2[i]为到达第一条生产线中第i个装配站的上一站是哪条生产线。f*为最终的最短时间,l*为产品最终在哪条生产线上完成生产。

算法思想如下:


        正如上面所说,我们先分别计算到两个装配线的站点1的最短路径,然后计算到站点2的最短路径,直到最终的最短路径。因为到一个站点的最短路径可以由前一个站点的最短路径加上前一个站点到本站点的最短路径。换句话说,到站点6的最短路径最优解包含到站点5的最优解,到站点5的最短路径最优解包含到站点4的最优解,……依此类推,最终可以变为到站点1的最优解。


算法实现如下:

#include<stdio.h>
#include<stdlib.h>

void print_route(int *l1,int *l2,int lfinal,int n);
int Fastest_way(int *a1,int n1,int *a2,int n2,int *t12,int n3,int *t21,int n4,int e1,int e2,int x1,int x2,int *l1,int *l2,int lfinal );

void main()
{

	int a1[]={7,9,3,4,8,4};//初始化各节点的时间消耗
	int a2[]={8,5,6,4,5,7};
	int t12[]={2,3,1,3,4};
	int t21[]={2,1,2,2,1};
	int e1=2;
	int e2=4;
	int x1=3;
	int x2=2;
	int n1=sizeof(a1)/sizeof(int);
	int n2=sizeof(a2)/sizeof(int);
	int n3=sizeof(t12)/sizeof(int);
	int n4=sizeof(t21)/sizeof(int);
	//printf("%d%d%d%d\n",n1,n2,n3,n4);
	int l1[6]={0};//第一个元素没有使用,每个元素的值代表前一次所在的生产线
	int l2[6]={0};
	int lfinal=0;//表示产品最终在哪个条线完成装配
	lfinal=Fastest_way(a1,n1,a2,n2,t12,n3,t21,n4,e1,e2,x1,x2,l1,l2,lfinal );
	print_route(l1,l2,lfinal,n1);	

}
/******************************************************\
函数功能:寻找最短时间路径
输入:各个节点的时间消耗
输出:最终完成装配所在的生产线
\******************************************************/
int Fastest_way(int *a1,int n1,int *a2,int n2,int *t12,int n3,int *t21,int n4,int e1,int e2,int x1,int x2,int *l1,int *l2,int lfinal )
{

	int f1[6]={0};
	int f2[6]={0};
	int final=0;//为总的最短时间消耗
	f1[0]=e1+a1[0];
	f2[0]=e2+a2[0];

	for(int i=1;i<n1;i++)
	{
		if((f1[i-1]+a1[i])<=(f2[i-1]+a1[i]+t21[i-1]))
		{
			f1[i]=f1[i-1]+a1[i];
			l1[i]=1;
		}
		else
		{
			f1[i]=f2[i-1]+a1[i]+t21[i-1];
			l1[i]=2;
		}

		if((f2[i-1]+a2[i])<=(f1[i-1]+a2[i]+t12[i-1]))
		{
			f2[i]=f2[i-1]+a2[i];
			l2[i]=2;
		}
		else
		{
			f2[i]=f1[i-1]+a2[i]+t12[i-1];
			l2[i]=1;
		}
	}

	if((f1[n1-1]+x1)<(f2[n1-1]+x2))
	{
		final=f1[n1-1]+x1;
		lfinal=1;
	}
	else
	{
		final=f2[n1-1]+x2;
		lfinal=2;
	}

	//for(int i=0;i<6;i++)
	//	printf("%d ",f1[i]);
	//printf("\n");

	//for(int i=0;i<6;i++)
	//	printf("%d ",f2[i]);
	//printf("\n");

	//for(int i=1;i<6;i++)
	//	printf("%d ",l1[i]);
	//printf("\n");

	//for(int i=1;i<6;i++)
	//	printf("%d ",l2[i]);
	//printf("\n");

	return lfinal;
}

/**************************************************\
函数功能:逆向打印装配所经过的线路节点
输入:    记录经过的节点的数组了l1和l2、最终完成装配所在的生产线
输出:    打印装配所经过的路程
\**************************************************/
void print_route(int *l1,int *l2,int lfinal,int n)
{
	int flag=0;
	printf("line: %d ,station: %d\n",lfinal,n);
	for(int i=n-1;i>0;i--)
	{
		if(i==n-1)
		{
			if(lfinal==1)
			{
			  printf("line: %d ,station: %d\n",l1[i],i);
			  if(l1[i]==1)
			      flag=1;
			  else
				  flag=2;
			}
			else
			{
			  printf("line: %d ,station: %d\n",l2[i],i);
			  if(l2[i]==1)
				  flag=1;
			  else
			      flag=2;
			}
		}
		else
		{
			if(flag==1)
	        {
			  printf("line: %d ,station: %d\n",l1[i],i);
			   if(l1[i]==1)
			      flag=1;
			  else
				  flag=2;
			}
			else
			{
			  printf("line: %d ,station: %d\n",l2[i],i);
			   if(l2[i]==1)
				  flag=1;
			  else
			      flag=2;
			}

		}

	}

}

原文:http://blog.csdn.net/tengweitw/article/details/16888601

作者:nineheadedbird

时间: 2024-11-08 19:16:53

【算法导论】动态规划算法之装配线调度的相关文章

算法 全排列 动态规划-算法实战题:创建摩尔斯电码字典计算第k个答案

问题描述 算法实战题:创建摩尔斯电码字典计算第k个答案 题目如下:计算第k个答案 摩尔斯电码字典 在没有电话的时代,摩尔斯电码是无线电传输领域中的一种常用代码.电码以短信号(短点,o)和长信号(长点,-)的不同组合表示各种文字.例如:o-表示英文字母J,而-表示英文字母M. 假设有一本以n个长点和m(n.m<=100)个短点组成的.包含所有信号的字典.例如:n=m=2,就会包含如下信号. --oo -o-o -oo- o--o o-o- oo-- 这些信号已按照字典顺序排列好了.-的ASKII码

【算法导论】排序 (二):堆排序

四.(1)堆排序 第一次听堆排序是在107lab听忠哥讲的,但是没讲怎么实现.那时刚看了数据结 构的二叉树,还以为要通过指针建立二叉树的方式来实现,觉得挺难的. 其实堆排序实现没有想象中 的那么难. "堆"这个词最初是在堆排序中提出来的,但后来就逐渐指"废料收集储存区",就像程 序设计语言Lisp和Java中所提供的设施那样.我们这里的堆数据结构不是废料收集存储区. 堆排序的 运行时间与归并排序一样为O(n lg n),  但是一种原地(in place)排序. (

【算法导论】排序(一)

虽然久闻大名,但第一次接触算法导论,是看了网易公开课MIT的<算法导论>课,记得 第一集就讲到了 插入排序和归并排序. 几个星期前也买了算法导论这本书,准备慢慢啃~ 这星期主要在看前两 部分,除了对于讲渐进时间.递归式分析这些东西感到云里雾里的,其它的都就 感觉越看越有觉得入 迷,果然不愧是一本经典之作 好吧,开始.本文主要是用C++把书中的算法实现,以及一些笔记. 一.插入排序. 插入算法的设计使用的是增量(incremental)方法:在排好子数组A[1..j- 1]后,将 元素A[ j]

【算法导论】动态规划之矩阵链乘法

       所谓矩阵链乘法是指当一些矩阵相乘时,如何加括号来改变乘法顺序从而来降低乘法次数.例如有三个矩阵连乘:A1*A2*A3,其维数分别为:10*100,100*5,5*50.如果按照((A1*A2)*A3)来计算的话,求(A1*A2)要10*100*5=5000次乘法,再乘以A3需要10*5*50=2500次乘法,因此总共需要7500次乘法.如果按照(A1*(A2*A3))来计算的话,求(A2*A3)要100*5*50=25000次乘法,再乘以A1需要10*100*50=50000次乘法

高效算法的常用技术(算法导论)

对于高效算法, 有些比较简单的技术, 如分治法, 随机化, 和递归求解技术. 这边介绍些更为复杂的技术, 动态规划, 贪心算法 当对于复杂问题设计算法时, 首先会想到使用分治法 来解决, 分而治之, 一个很有哲理性的思路, 再复杂的问题都可以不断分解到你可以轻松解决的粒度, 把所有简单问题都解决完后, 组合在一起就得到了复杂问题的解, 可以看出其中典型的递归求解的思路. 使用分治法的要求, 各个子问题是独立 的 (即不包含公共的子子问题,子问题不重叠 ). 如果子问题重叠, 用分治法就比较低效,

算法——动态规划算法

动态规划法基本思想:将原问题分解为相似的子问题,在求解的过程中通过子问题的解求出原问题的解.著名的应用实例有:求解最短路径问题,背包问题,项目管理,网络流优化等. 个人对动态规划的理解,主要就是避免重复计算.就是那些曾经发生过的事情,曾经计算过的值先保存下来,然后再次遇到相同的子问题的时候,直接用保存好的值给出,不再进行计算. 有一个很简单的例子,关于斐波那契数列.   什么是斐波那契数列?根据维基百科的解释是这样的, 费波那西数列(Fibonacci Sequence),又译费波拿契数.斐波那

《算法导论(原书第3版)》一第1章 算法在计算中的作用 - 1.1 算法

第1章 算法在计算中的作用 什么是算法?为什么算法值得研究?相对于计算机中使用的其他技术来说算法的作用是什么?本章我们将回答这些问题. 1.1 算法 非形式地说,算法(algorithm)就是任何良定义的计算过程,该过程取某个值或值的集合作为输入并产生某个值或值的集合作为输出.这样算法就是把输入转换成输出的计算步骤的一个序列. 我们也可以把算法看成是用于求解良说明的计算问题的工具.一般来说,问题陈述说明了期望的输入/输出关系.算法则描述一个特定的计算过程来实现该输入/输出关系. 例如,我们可能需

用动态规划算法对最大子串问题的java实现

最大字串问题描述大概就是给定2个字符串,找出他们两个共有的最长字符串.比如一个是"tabcfg"另外一个"abckj"那么最大子串就是"abc". 动态规划算法最重要的就是分解问题,找出递归.说一下我的思考思路,首先拿到2个字符串,如何找到最长子串呢? 1.假设他们(字符串a,b)的头字母不相同的话,那么分别去掉首字母比较,也就是说用a.subString(1)和b比较,用 b.subString(1)和a比较,最长子字符串没变吧?答案是肯定的.

C++编程:C++归并排序实现(算法导论)

  算法导论上的下标是从1开始的,但是为了和c++ STL的设计思想一致,所有函数的实现统一用左闭右开区间.中间修改了很多次,因为下标修改不是很容易就改掉的,需要始终维持循环不变式,稍微一个步骤出错就会使结果有些错误. #include #include #include #include using namespace std; void merge(int *A, int p, int q, int r) { //merge [p, r),左闭右开区间 int n1 = q - p, n2