问题描述:
给定n个作业的集合J={J1,J2,…,Jn}。每一个作业有两项任务分别在两台机器上完成。每个作业必须先由机器1处理,再由机器2处理。作业Ji需要机器j的处理时间为tji,i=1,2,…n,j=1,2。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。则所有作业在机器2上完成处理的时间和f=F21+F22+…+F2n称为该作业调度的完成时间和。 批处理作业调度问题要求,对于给定的n个作业,制定最佳的作业调度方案,使其完成时间和最小。
题目类型:回溯
代码如下:
方法一:回溯+全排列
#include<stdio.h>
#define N 100
int n;
int a[N]={0},b[N]={0}; //作业在两台机器上的运行时间
int c[N]={0},bestc[N]={0};
int f1=0,f2[N];
int count=0;
int best=1000000;
void swap(int &a,int &b)
{
int temp=a;
a=b;
b=temp;
}
void traceback(int num)
{
int i;
if(num>n) {
if(count<best) { //更新最小时间和,最佳解决方案
best=count;
for(i=1;i<=n;i++)
bestc[i]=c[i];
}
return ;
}
for(i=num;i<=n;i++) {
f1+=a[c[i]]; //第一台机器一直在运行
f2[num]=(f2[num-1]>f1 ? f2[num-1] : f1) + b[c[i]];//关键代码(用数组标记,回溯时无需还原)
count+=f2[num];//count只求第二台机器的时间和
swap(c[i],c[num]);//全排列进行交换
if(count<best)
traceback(num+1);
swap(c[i],c[num]);
count-=f2[num];
f1-=a[c[i]];
}
}
int main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++) {
scanf("%d%d",&a[i],&b[i]);
}
for(i=1;i<=n;i++) { //全排列的初始化
c[i]=i;
}
traceback(1);
printf("%d\n",best);
for(i=1;i<=n;i++) {
printf("%d ",bestc[i]);
}
printf("\n");
return 0;
}
方法二:个人对于回溯题目的通用解题思路,未使用全排列,也未使用一维数组标记f2[]
#include<stdio.h>
#define N 100
int n;
int a[N]={0},b[N]={0};
int c[N]={0},bestc[N]={0};
int flag[N]={0};//用一个数组标记已经使用过的数字,1表示已经使用,0表示未使用
int f1=0,f2=0,count=0;//f2使用变量,而非数组
int best=1000000;
void traceback(int num)
{
int i;
if(num>n) {
if(count<best) {
best=count;
for(i=1;i<=n;i++)
bestc[i]=c[i];
}
return ;
}
//与全排列不同的地方
for(i=1;i<=n;i++) { //一共有1--n种作业可供选择
if(flag[i]==0 && count<best) {
flag[i]=1;
f1+=a[i];
/*全排列时用的一维数组f2[],回溯时无需还原,现在使用普通的变量f2,要进行记录*/
int old=f2;
f2=(f2>f1 ? f2 : f1) + b[i];
c[num]=i;
count+=f2;
traceback(num+1);
count-=f2;
//c[num]=i;无需还原
f2=old;
f1-=a[i];
flag[i]=0;
}
}
}
int main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++) {
scanf("%d%d",&a[i],&b[i]);
}
traceback(1);
printf("%d\n",best);
for(i=1;i<=n;i++) {
printf("%d ",bestc[i]);
}
printf("\n");
return 0;
}