活动选择问题

问题是这样的,给定一系列的活动,给出可以完成的最多的活动的数量。做法直接贪心即可,按照结束时间排序,然后选择即可

代码如下

#include <iostream>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <queue>
#include <stack>
#include <string>
#include <climits>
#include <algorithm>
#include <sstream>
#include <functional>
#include <bitset>
#include <numeric>
#include <cmath>
#include <regex>
#include <iomanip>
#include <cstdlib>
#include <ctime>

using namespace std;


//下面是活动选择问题的DP方法和贪心算法

//表示一个活动的开始结束时间
const int SIZE = 100;
struct Activity
{
    int beginTime;
    int endTime;
    Activity(int x, int y)
    {
        beginTime = x;
        endTime = y;
    }
};

//按照结束事件递增排序
bool cmp(Activity x, Activity y)
{
    return x.endTime <= y.endTime;
}

void GreedByLoop(vector<Activity> All);
void DP(vector<Activity> All);
void printRes(int c[SIZE][SIZE], int x, int y, vector<int> &res);


int main()
{
    const int SIZE = 12;
    //一共11个活动,最大的活动兼容集合是{a1,a4,a8,a11}和{a2,a4,a9,a11}
    //第一个的开始时间和结束时间0 0 是为了便于计算,特意添加的一个虚拟事件
    int beg[SIZE] = { 0,1,3,0,5,3,5,6,8,8,2,12 };
    int end[SIZE] = { 0,4,5,6,7,8,9,10,11,12,13,14 };

    vector<Activity> AllActivity;
    for (int i = 0; i<SIZE; i++)
        AllActivity.push_back(Activity(beg[i], end[i]));
    sort(AllActivity.begin(), AllActivity.end(), cmp);

    GreedByLoop(AllActivity);

    Activity one = Activity(15, 15);
    AllActivity.push_back(one);
    DP(AllActivity);
    AllActivity.pop_back();

    system("pause");
}

//Greedy算法循环实现
//主要思想是选择尽量早早介绍的活动,这样可以留
//更多的剩余时间去安排其他更多的活动,所以活动都是按照结束时间递增排序的
//所以第一个活动必然在活动列表之内,然后依次安排其余的活动
void GreedByLoop(vector<Activity> All)
{
    vector<int> res;
    int k = 1;
    res.push_back(k);
    for (int i = 2; i<All.size(); i++)
    {
        //寻找下一个最早结束的活动
        if (All[i].beginTime >= All[k].endTime)
        {
            res.push_back(i);
            k = i;
        }
    }
    cout << endl << "By Loop Final Res is Following: " << endl;
    for (int i = 0; i<res.size(); i++)
        cout << res[i] << "  ";
    cout << endl;
}

// c[i][j]表示活动i介绍之后到活动j开始之前的活动的数量,
// c[i][j]=c[i][k]+c[k][j]+1; 表示存在活动k在i和j之间,加入k不影响活动i和j,
//那么就可以分解为c[i][k],c[k][j]两个子问题,
//需要向头部和尾部添加两个虚拟活动
void DP(vector<Activity> All)
{
    int c[SIZE][SIZE] = { 0 };
    int ret[SIZE][SIZE] = { 0 };

    //要考头部的虚拟活动,所以j从1开始,到All.size()结束
    //这个要求i<j,而且k是在i和j之间,所以这个直接三层循环即可,这个和folyed算法的最优子结构很像
    for (int j = 1; j<All.size(); j++)
    {
        for (int i = 0; i<j; i++)
        {
            for (int k = i + 1; k<j; k++)
            {
                if (All[k].beginTime >= All[i].endTime && All[k].endTime <= All[j].beginTime)
                {
                    if (c[i][k] + c[k][j] + 1 > c[i][j])
                    {
                        c[i][j] = c[i][k] + c[k][j] + 1;

                        //记录分叉位置,便于递归查找
                        ret[i][j] = k;
                    }

                }
            }
        }
    }
    int FinalNum = c[0][All.size() - 1];
    cout << endl << "By DP Nums is : " << FinalNum << endl;

    vector<int> res;
    printRes(ret, 0, All.size() - 1, res);
    cout << "By DP Final Res is Following: " << endl;
    for (int i = 0; i<res.size(); i++)
        cout << res[i] << "  ";
    cout << endl;

}

//递归查询
void printRes(int c[SIZE][SIZE], int x, int y, vector<int> &res)
{
    if (c[x][y] != 0)
    {
        int k = c[x][y];
        res.push_back(k);
        printRes(c, x, k, res);
        printRes(c, k, y, res);
    }
}