代码改变世界

区间调度问题(最大利润作业调度问题)

2011-12-01 11:17  Demote  阅读(732)  评论(0编辑  收藏  举报


转自: http://blog.sina.com.cn/pc178


区间调度问题 ,最大利润作业调度问题
// 首先介绍什么是贪心算法(贪婪算法)
//
// 所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,
// 他所做出的仅是在某种意义上的局部最优解。
//   贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最
// 优解或者是整体最优解的近似解。
//   贪心算法的基本思路如下:
//   1.建立数学模型来描述问题。
//   2.把求解的问题分成若干个子问题。
//   3.对每一子问题求解,得到子问题的局部最优解。
//   4.把子问题的解局部最优解合成原来解问题的一个解。
//   实现该算法的过程:
//    从问题的某一初始解出发;
//    while 能朝给定总目标前进一步 do
//    求出可行解的一个解元素;
//    由所有解元素组合成问题的一个可行解;
//
//
// 例题:
// 现有1,2,3......n个任务,各个任务的开始时间和结束时间都是在时间段[ timestart, timeend]内随机生成的,
// 要求在ts-te时间段内能够完成最多的任务,并求出这些任务。

//上述实现对于这类各任务的重要性相同的情况是适用的,但实际上可能有些任务比较急迫(或更为重要)。例如:任务1做了
//可以获利10,需要时间为 5;任务2做了可以获利20,需要时间为 15。如果做任务1、任务2是互斥,那么我们我们做哪个呢?
//答案是:需要从整体考虑,显然就单个任务的性价比看,做任务1更划算,但我们不能保证任务1过后的 10的时间里能获利10。
//那么对于这样的问题贪心算法就不再适用了,因为贪心算法要求整个问题的最优解一定由在贪心算法中存在的子问题的最优解
//得来的,通俗的讲,是种追求现利益最大的人,通常被认为目光短浅!
//对于这类问题,常采用另一种类似算法:动态规划。动态规划是一种将问题实例分解为更小的、相似的子问题,并存储子问
//题的解而避免计算重复的子问题,以解决最优化问题的算法策略。将问题分解为相似的子问题的做法和贪心算法是类似的,
//但采用动态规划的全局最优解是:全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解。
//详细内容请参考资料。
//在此,我们假设对上述问题的每个任务都有一个报酬(wage),并简单的假设任务i的wage值为:i % (TOTALTASKS/2)。
//找出在时间段[ timestart, timeend]内使总报酬(wage)最大的任务集,其实上面的贪心算法可以看做是各任务报酬都
//相同的情况。
#define max(a, b) (((a) > (b)) ? (a) : (b))
void IntervalScheduler_DynamicProgramming(task tasks[], int total) //动态规划实现
{
int *optimization = new int[total+1];
int *above = new int[total+1]; //above[i]表示在以按任务结束时间从小到大排序的任务中,在任务i-1开始之前
//就已经结束的离任务i-1最近的任务,而above[0]为0,表示前面的以无任务,这样
//表示是为了,实现使算法更容易实现,所以可能有些难以理解
int i, j;
QuickSort_End(tasks, 0, total-1); //以任务的结束时间从小到大排序
above[0] = 0; //计算各个任务的above值
for (i=1; i<=total; i++){
for (j = i-1; j>=1 && tasks[i-1].timestart < tasks[j-1].timeend; j--);
if (j < 1)
above[i] = 0;
else
above[i] = j;
}
//动态规划的主要思路,这里的解法是:OPT(i) = MAX{ OPT(i-1), OPT(above[i])+tasks[i].wage };
//但这个任务属于最优解里时就有,OPT(i) = OPT(above[i])+tasks[i].wage;不属于最优解里是:
//OPT(i) = OPT(i-1)。这就是这要思路,很浅显,如果用递归的话很容易实现,主要是效率问题,
//动态规划就是避免递归的重复求值!
//和上面的above[i]一样,为了更简单的实现,optimization[i]为任务0...i-1的最优解,optimization[0]=0
//用来表示结束,这里的下标0 不表示任务0,而表示任务-1,没有任务-1,所以没意义!
optimization[0] = 0;
for (i=1; i<=total; i++)
optimization[i] = max(optimization[i-1], optimization[above[i]] + tasks[i-1].wage);
// 运用“反向追踪法”追踪动态规划生成的中间过程,以反向的序列输出,由动态规划生成的结果,一般都可以
//采用这种方式追踪中间的过程!~!~
cout << endl << "反向追踪法探查生成结果" << endl;
for (i = total; i>0; )
if (optimization[i] == optimization[i-1]){
i--;
}else{
cout <<"任务标志:" << tasks[i-1].tasksign <<" 任务开始时间:" << tasks[i-1].timestart
<< " 任务结束时间:" << tasks[i-1].timeend << " 报酬: " << tasks[i-1].wage << endl;
i = above[i];
}
}
//关于这个问题就讨论到这里了,也许你对动态规划带权区间调度还是不甚了解,那你还等什么,看书去吧!了解下可以
//用动态规划解决的问题,如典型的背包问题,Bellman-Ford最短路径算法,序列比对等!