(C/C++)区间调度问题的解决及输出:动态规划、贪心算法(递归、递推)

给定n个活动,其中的每个活动ai包含一个起始时间si与结束时间fi。设计与实现算法从n个活动中找出一个最大的相互兼容的活动子集S。
要求:分别设计动态规划与贪心算法求解该问题。其中,对贪心算法分别给出递归与迭代两个版本的实现。

思路

动态规划

动态规划的思路则对此问题来说较为复杂,定义Sij为在i任务结束之后,j任务开始之间所包含的任务的子集。定义两个虚拟任务ai、an+1,则问题对应了S0,,n+1的解。Sij的元素数量则对应了任务的数量。通过递归方程可知复杂度为O(n3),可通过设定另一个二维数组以输出元素。

递归方程

贪心算法

贪心算法的思路很简单,非空子集Sij,若am结束的时间最早,则有:
贪心准则:am一定属于Sij的某个最优解且Sim为空。
贪心准则的证明:
Aijj为Sij最优解,另其中的任务按照结束时间递增排序,令ak是Aij的第一个结束的任务,如果ak=am,则证明成立。否则我们将ak用am替换,则它成为了另一个解A'ij,同样是最优解。
所以即将任务以结束时间递增排序,第一个结束的任务一定在最优解中,依次找出子问题中最先结束,且开始时间在前一个解最后一个任务结束之间之后。复杂度为O(n)。同时很容易得出有递归和递推两种形式,一般采用递推。

贪心算法

递归

递推

#include <iostream>
#include <vector>
#define TASK_COUNT 8
using namespace std;
int finish[TASK_COUNT + 2] = { -1,2,4,6,7,8,9,12,15,255};
int start[TASK_COUNT + 2] = { -1,1,2,3,3,1,7,10,13,255};
int _count[TASK_COUNT + 2][TASK_COUNT + 2];
int p[TASK_COUNT + 2][TASK_COUNT + 2];
void recursive(int* start,int* finish,int begin,int end) {
    int m = begin + 1;
    while (m <= end) {
        if (start[m] >= finish[begin]) {
            break;
        }
        m++;
    }
    if (m <= end) {
        cout << m << " ";
        recursive(start, finish, m, end);
    }
}

void iterative(int* start, int* finish) {
    int len = TASK_COUNT;
    int pre = 0;
    for (int i = 1; i <= len; i++) {
        if (start[i] >= finish[pre]) {
            cout << i << " ";
            pre = i;
        }
    }
}


void dp(int* start, int* finish) {
    for (int len = 2; len <= TASK_COUNT + 2; len++) {
        for (int begin = 0; begin <= TASK_COUNT + 1; begin++) {
            int end = begin + len - 1;
            int max = 0;
            int slice = 0;
            for (int k = begin + 1; k <= end - 1; k++) {
                if (start[k] >= finish[begin]&&finish[k]<=start[end]) {
                    int temp = _count[begin][k] + _count[k][end] + 1;
                    if (temp > max) {
                        max = temp;
                        slice = k;
                    }
                }
            }
            p[begin][end] = slice;
            _count[begin][end] = max;
        }
    }
}

void printDp( int begin, int end) {
    int pos = p[begin][end];
    if (pos == 0)
        return;
    cout << pos << " ";
    printDp(begin, pos);
    printDp(pos, end);
    
}

int main(void) {
    for (int i = 0; i <= TASK_COUNT; i++) {
        cout << i << ":";
        for (int j = 0; j < start[i]; j++) {
            cout << "  ";
        }
        for (int j = start[i]; j < finish[i]; j++) {
            cout << "■";
        }
        cout << endl;
    }
    recursive(start, finish, 0, TASK_COUNT);
    cout << endl;
    iterative(start, finish);
    cout << endl;
    for (int i = 0; i <= TASK_COUNT + 2; i++) {
        for (int j = 0; j <= TASK_COUNT + 2; j++) {
            _count[i][j] = 0;
            p[i][j] = 0;
        }
    }
    dp(start, finish);
    cout << _count[0][TASK_COUNT + 1] << endl;
    printDp(0, TASK_COUNT + 1);
    system("pause");
    return 0;
}

运行结果

运行结果
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在学习「数据结构和算法」的过程中,因为人习惯了平铺直叙的思维方式,所以「递归」与「动态规划」这种带循环概念(绕来绕...
    五分钟学算法阅读 1,929评论 1 34
  • 分治算法 一、基本概念 在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题...
    Java资讯库阅读 9,793评论 0 14
  • 分治算法 一、基本概念 在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题...
    木叶秋声阅读 5,318评论 0 3
  • 1、前言 求解最优化问题的算法通常会经历一系列步骤,在每个步骤都会面临多种选择,而许多最优化问题并不需要计算每个选...
    某昆阅读 1,651评论 1 5
  • 很少有导演可以把文艺片拍的像商业片那么挣钱的,经常会看到惨淡的文艺片票房几乎逼得制片方下跪。我一些喜欢文艺片...
    九转惊鸿阅读 316评论 0 2