有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
上图省略了输出的一些数组的信息。