c语言-最优批处理问题

问题描述:
在一台超级计算机上,编号为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" , &times[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 );
}

实验结果:
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,批处理文件是一个文本文件,这个文件的每一行都是一条DOS命令(大部分时候就好象我们在DOS提示符下执行的命令行一样),你可以使用DOS下的Edit或者Windows的记事本(notepad)等任何文本文件编辑工具创建和修改批处理文件。 其次,批处理文件是一种简单的程序,可以通过条件语句(if)和流程控制语句(goto)来控制命令运行的流程,在批处理中也可以使用循环语句(for)来循环执行一条命令。当然,批处理文件的编程能力与C语言等编程语句比起来是十分有限的,也是十分不规范的。批处理的程序语句就是一条条的 DOS命令(包括内部命令和外部命令),而批处理的能力主要取决于你所使用的命令。 第三,每个编写好的批处理文件都相当于一个DOS的外部命令,你可以把它所在的目录放到你的DOS搜索路径(path)中来使得它可以在任意位置运行。一个良好的习惯是在硬盘上建立一个bat或者batch 目录(例如C:\BATCH),然后将所有你编写的批处理文件放到该目录中,这样只要在path中设置上c:\batch,你就可以在任意位置运行所有你编写的批处理程序。 第四,在DOS和Win9x/Me系统下,C:盘根目录下的AUTOEXEC.BAT批处理文件是自动运行批处理文件,每次系统启动时会自动运行该文件,你可以将系统每次启动时都要运行的命令放入该文件中,例如设置搜索路径,调入鼠标驱动和磁盘缓存,设置系统环境变量等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值