问题描述:
在一台超级计算机上,编号为1,2,…,n的n个作业等待批处理。批处理的任务就是将这n个作业分成若干批,每批包含相邻的若干作业。从时刻0开始,分批加工这些作业。在每批作业开始前,机器需要启动时间S,而完成这批作业所需的时间是单独完成批中各个作业需要时间的总和。单独完成第i个作业所需的时间是ti,所需的费用是它的完成时刻乘以一个费用系数fi。同一批作业将在同一时刻完成。例如,如果在时刻T开始一批作业x,x+1,…,x+k,则这一批作业的完成时刻均为T+S+(tx+tx+1+…+tx+k)。最优批处理问题就是要确定总费用最小的批处理方案。例如,假定有5个作业等待批处理,且S=1,(t1,t2,t3,t4,t5)=(1,3,4,2,1),(f1,f2,f3,f4,f5)=(3,2,3,3,4)。如果采用批处理方案{1,2},{3},{4,5},则各作业的完成时间分别为(5,5,10,14,14),各作业的费用分别为(15,10,30,42,56),因此,这个批处理方案总费用是153。
算法设计:
对于给定的待批处理的n个作业,计算其总费用最小的批处理方案。
数据输入:
由文件Input.txt提供输入数据。文件的第1行是待批处理的作业数n,第2行是启动时间S。接下来每行有2个数,分别为单独完成第i个作业所需的时间是ti和所需的费用系数fi。
结果输出:
将计算出的最小费用输出到文件output.txt中。
输入文件示例
Input.txt output.txt
5 153
1
1 3
3 2
4 3
2 3
1 4
设计思路:
分析题目,因为每批完成的作业必须相邻,即只能{1,2}{3},不能{1,3}{2},因此可以用动态规划来解决。设置一个minBatchCost数组,minBatchCost[i][t]表示处理第i到第n-1个作业,从时间t开始的最小开销。调用minBatchCost[0][0],即为处理第0个作业到第n-1个作业,从0时刻开始的最小开销,即为题目所求。
对于待处理作业i到n-1,不断求出第i到i+1,i到i+2……i到n-1的最小花费并填入minBatchCost。只要没有计算到第n-1个作业,就递归处理后面的任务,直到处理完所有的作业。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<limits.h>
#define MAXN 20
#define MAXT 20
//定义全局变量:作业总数n,启动时间S,每个作业花费时间的数组times,
//费用系数costs,minBatchCost[i][t]表示从t时刻开始处理第i到第n个作业总花费
int n = 0;
int S = 0;
int times[MAXN];
int costs[MAXN];
int minBatchCost[MAXN][MAXT];
//prefixTime[i]表示处理前i个作业花费的总时间,Cost类似
int prefixTime[MAXN];
int prefixCost[MAXN];
//读取文件中的作业处理时间和作业完成费用并记录入time[]和fare[]
void readTimeAndCost ()
{
FILE*f = fopen ( "D:\\学习\\大三上\\算法设计与分析\\实验四报告+附件\\input(2).TXT" , "r" );
if (f == NULL)
{
printf ( "读取文件错误" );
return;
}
fscanf ( f , "%d" , &n );
fscanf ( f , "%d" , &S );
for (int i = 0; i < n; i++)
{
fscanf ( f , "%d" , ×[i] );
fscanf ( f , "%d" , &costs[i] );
}
}
//初始化minBatchCost和prefixTime,prefixCost
void initData ()
{
memset ( minBatchCost , -1 , sizeof ( minBatchCost ) );
for (int i = 0; i < n; i++)
{
prefixTime[i] = times[i];
prefixCost[i] = costs[i];
if (i != 0)
{
prefixTime[i] = prefixTime[i - 1] + times[i];
prefixCost[i] = prefixCost[i - 1] + costs[i];
}
}
}
//求处理第x到第y个任务的总时间和费用系数,最容易想到的当然是直接for循环times[x]+…+times[y],
//不过这样的话会有很多不需要的重复计算,因此我们另外定义一个prefixTime和prefixCost数组来计算
//求某次批处理第x到第y个作业花费的总时间
int getBatchTimes (int x,int y)
{
return prefixTime[y] - prefixTime[x] + times[x];
}
//求某次批处理第x到第y个作业花费的总费用系数
int getBatchCosts ( int x , int y )
{
return prefixCost[y] - prefixCost[x] + costs[x];
}
//求最优批处理方案
int optimalBatch ( int x , int t )
{
//动态规划的保存了已经计算过的部分,因此我们先判断是否已经求出minBatchCost[x][t]
if (minBatchCost[x][t] == -1)
{
int minCost = INT_MAX;
//处理第x到第y个作业
for (int y = x; y < n; y++)
{
int batchCosts = getBatchCosts ( x , y );
int endBatchTime = t + S + getBatchTimes ( x , y );
int totalCosts = batchCosts * endBatchTime;
//如果y+1<n,说明作业还没处理完,则剩余任务递归调用函数求出剩余任务的最小花费加上前面的花费即为总花费
if (y + 1 < n)
{
totalCosts += optimalBatch ( y + 1 , endBatchTime );
}
minCost = ( minCost < totalCosts ? minCost : totalCosts );
}
minBatchCost[x][t] = minCost;
}
return minBatchCost[x][t];
}
int main ()
{
readTimeAndCost ();
initData ();
int cost = optimalBatch ( 0 , 0 );
printf ( "总费用最小的批处理方案的总费用为:%d\n" , cost );
//写入文件函数较简单不单独写一个函数
FILE* f = fopen ( "D:\\学习\\大三上\\算法设计与分析\\实验四报告+附件\\output(2).TXT" , "w" );
fprintf ( f , "%d" , cost );
fclose ( f );
}
实验结果: