用动态规划求解调度问题

有n个工件,j1, j2, …, jn, 每个工件有一个给定的最小加工时间pj , 以及一个权重wj

1<=j <=n。有一个用来加工这些工件的机器。假设这个机器一次可以加工任意多的工件, 同时加工的工件组成一个批B。在同一批B里加工的工件必须同时开始,同时结束,即它们有同样的开始时间sj, 同样的结束时间Cj= sj + p(B),j∈B, 这里p(B)=max{pj} j∈B。 从一批工件开始加工到结束中间不允许中断。

 

请问:如何给这些工件分批,以及如何对这些批进行排序使得所有工件的加权完成时间之和 Σwj*Cj (j=1到n)   最小。

 

请设计一个算法并求解下面的实例:

 

n = 50

最小加工时间pj依次是:

1,5,2,4,5,9,5,10,5,6,6,7,8,7,11,6,6,7,11,10,7,12,8,8,8,6,5,13,14,9,8,9,3,3,11,2,9,13,4,6,4,4,8,9,6,15,5,7,9,3

 

权重wj依次是:

2,3,2,5,4,4,6,8,5,7,7,4,6,9,12,4,4,3,5,5,3,6,9,5,9,12,3,7,12,11,11,3,4,5,3,12,2,6,6,4,9,15,12,4,8,31,1,12,3,4


思考:

第一步:按照最小加工时间 ,先从小到大排序(思考:为什么按照最小加工时间呢? 如果不是按照最小时间从小到大,举个例子,如果有一个时间为1,权重为15的没有在最开始的时候加工,那么它的加权时间一定要大于其在最开始完成的加权时间,(因为1是最小的时间片段,所以最开始加工的话不会耽误更多的时间。)那么这样肯定就不会让总的加权时间和最小了。)

第二步:

求状态转移方程

minTime[]  存放每一步得到的最小的加权时间和

finish[]  存放每一步得到的结束时间

 

N=1时,p:1  ,w:2; p1  time(1)=w1*p1 = 2               c(1)=1

N=2,p:1,2  w:2,2   time(2)=min{time(1)+[c(1)+p2]*w2 ,p2*(w1+w2)} = min{8,8} = 8//如果相同,取第二种,因为第二种前面用的总时间要少 c(2)=2

N=3,p:1,2,2  w:2,2,2  time(3) = min{time(2)+[c(2)+p3]*w3 ,time(1)+[c1+p3]*(w2+w3),p3*(w1+w2+w3)} = min{16,14,12} = 12                  c(3)= 2


N=n, time(n)=min{time(n-1)+[c(n-1)+pn]*wn,time(n-2)+[c(n-2)+pn]*[w(n-1)+wn],……,time(1)+[c1+pn]*(wn+w(n-1)+……+w2),pn*(w1+w2+w3+…+wn)}

 


代码如下:


package test;

import java.util.Stack;


/*
 * 思路
 * 1.按照最小加工时间从小到大排序
 * 2.找到状态转移方程
 */
public class DP_Schedule {
	
	static int [] time ={1,5,2,4,5,9,5,10,5,6,6,7,8,7,11,6,6,7,11,10,7,12,
		8,8,8,6,5,13,14,9,8,9,3,3,11,2,9,13,4,6,4,4,8,9,6,15,5,7,9,3};
	static int [] weight = {2,3,2,5,4,4,6,8,5,7,7,4,6,9,12,4,4,3,5,5,3,6,
		9,5,9,12,3,7,12,11,11,3,4,5,3,12,2,6,6,4,9,15,12,4,8,31,1,12,3,4};
	
//	static int [] index=new int[time.length];
	static job [] jobs = new job[time.length];
	
	static int finish[] =new int[jobs.length];		//记录有i个工件时完成时候的时间
	static int minTime[] =new int[jobs.length];		//记录有i个工件时需要的最小时间
	static int index [] = new int[jobs.length];		//记录第i个工件那批的上一批的最后一个的下标(排好序之后的下标)
	
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		job [] jobs = generate(time,weight);
		sort(jobs);
		System.out.println();
//		for(int i=0;i s =new Stack();
		//用一个栈循环将这些工件的每一批的最后一个的下标加入,就会找到分批的位置
		s.push(n);
		while(arr[n] != 0){
			s.push(arr[n]);
//			System.out.println(arr[n]);
			n = arr[n];
//			System.out.println(arr[n]);
		}
		
		int groupNum = 1,count = 1;
		int start = 0;	//输出的位置
		while(!s.empty()){
	//		System.out.println(s.size());
			int len = s.pop();
	//		System.out.println(len);
			System.out.println("**********************************************************************");
			System.out.println("第 "+groupNum+" 批如下:");
			for(int i =start;i<=len ;i++){
				System.out.println("第 "+jobs[i].index +" 个工件:    所需时间: "+jobs[i].time+"    权重:  "+jobs[i].weight+"     "+count++);
			}
			start = len+1;	//下一次从len这个位置开始输出
			groupNum++;		//批次加一
		}
		
	}
	
	//计算最小值的选择表达式
//	public static void cal(int n){
//		int 
//	}
	
	//根据输入的时间、权重两个数组建立一个job的数组
	public static job [] generate(int time[] ,int weight[]){
		
		for(int i = 0;i time[j+1]){
					temp = time [j+1];
					time[j+1] = time[j];
					time[j] = temp;
					
					jb = arr[j+1];
					arr[j+1] = arr[j];
					arr[j] = jb;
				}
			}
		}
		
		for(int i=0;i

仅仅是我自己的见解,如果有什么地方不对或者可以改进的地方,欢迎大家提出来~~~




分割线


经过同学的讨论与建议,我发现上面的算法是错误的,少算了一种情况,

举个例子:

比如前面有两个零件,最优解是两个零件分开来做,我在加入第三个的时候,没有考虑下面这种情况:把前两个零件合起来作为一批然后第三个作为一批。这种情况必须要考虑,因为前两个零件合起来的话结束时间是要比两个分开来做小的,尽管对于只有前面两个零件时的情况两个分开做是最优。下面是我改进后的代码:


public class DP_Schedule {
	
	static int [] time ={1,5,2,4,5,9,5,10,5,6,6,7,8,7,11,6,6,7,11,10,7,12,
		8,8,8,6,5,13,14,9,8,9,3,3,11,2,9,13,4,6,4,4,8,9,6,15,5,7,9,3};
	static int [] weight = {2,3,2,5,4,4,6,8,5,7,7,4,6,9,12,4,4,3,5,5,3,6,
		9,5,9,12,3,7,12,11,11,3,4,5,3,12,2,6,6,4,9,15,12,4,8,31,1,12,3,4};

	static job [] jobs = new job[time.length];
	
	
	static int [][] res =new int[jobs.length][jobs.length];
	static int [][] fin = new int[jobs.length][jobs.length];
	static int [][] pre=new int[jobs.length][jobs.length];
	
	public static void main(String[] args) {
		job [] jobs = generate(time,weight);
		sort(jobs);
		DPsolve2();
		int [] result = findMIn(50-1);
		System.out.println("*******************************************");
		System.out.println(result[0]);
		System.out.println("注意:批次的顺序是从下往上的");
		divide2(pre, result[1], result[2]);
	}
	
	
	
	//往右走是0
	//往下走是1
	public static void DPsolve2(){
		
		
		for(int i=0,weight=0;i0){
						x--; 
						we +=jobs[x+j].weight;
					}
				//	we +=jobs[x+k].weight;
					res[j][k] = res[j-1][x]+we*(jobs[j+k].time+fin[j-1][x]);
					fin[j][k] = jobs[j+k].time+fin[j-1][x];
					pre[j][k] =0;
				}
			}
		}
		
		System.out.println("**********************************");
		for(int i=0;i time[j+1]){
					temp = time [j+1];
					time[j+1] = time[j];
					time[j] = temp;
					
					jb = arr[j+1];
					arr[j+1] = arr[j];
					arr[j] = jb;
				}
			}
		}
		
		for(int i=0;i



上图省略了输出的一些数组的信息。

你可能感兴趣的:(算法)