活动选择问题的动态规划解

/*
 * 题目大意: DP方法解活动选择问题

   解题思路: s(i,j)表示所有活动的集合{fi <= sk < fk <= sj},即活动ai和aj之间,所有与这两个活动
             兼容的活动组成的子集。c[i,j]表示该集合的大小。


   核心内容: 对问题的建模,采用该模型分析既可以使用这种DP解决,也可以类似LIS问题的DP解决,还可以
             贪心解决。但是所有的这些都建立在正确的建立了数学模型的基础之上。

             LIS DP方法: C[j]表示集合s(0, j)的最大兼容子集大小,C[j] = max({1<=k<j, C[k]} + 1)
             
             递归式的来由: 如果aj兼容于ak,则凡是与ak兼容的子集s(0, k)必然与aj兼容 
                           -- 由于集合已经按照起始时间排序。所以可以采用LIS方式DP

             贪心方法: 由第一种DP实现方式推得。k的选择直接采用贪心方法取集合s(i, j)中最靠前的元素。
             
*/

#include <iostream>
#include <cstdio>
#include <algorithm>

namespace {
    using namespace std;

    typedef struct ACTIVITY
    {
        int s, f;
    }ACTIVITY_S;

    int comp(const void *op1, const void *op2)
    {
        ACTIVITY_S *a1 = (ACTIVITY_S *)op1;
        ACTIVITY_S *a2 = (ACTIVITY_S *)op2;

        if (a1->s < a2->s) return -1;
        if (a1->s == a2->s) return 0;
        if (a1->s > a2->s) return 1;
    }

    const int ACTIVITY_NUM_MAX = 32;
    ACTIVITY_S activities[ACTIVITY_NUM_MAX+2];

    const int ACTIVITY_TIME_MAX = 100;
    int c[ACTIVITY_NUM_MAX+1][ACTIVITY_NUM_MAX+2]; // 默认初始化为0

    int activity_num;
    
    int activity_select_dp()
    {
        qsort(&activities[1], activity_num, sizeof(activities[0]), comp);
        
        activities[0].f = 0; activities[activity_num+1].s = ACTIVITY_TIME_MAX; // 起始和结尾添加的辅助活动
        for (int i=activity_num; i>=0; i--)
        {
            for (int j=i+1; j<=activity_num+1; j++ ) // s(i, i)等于空集
            {
                for (int k=i+1; k<j; k++)
                {
                    if (activities[i].f<=activities[k].s && activities[k].f<=activities[j].s) // s(i, j)不为空
                    {
                        c[i][j] = max(c[i][j], c[i][k]+1+c[k][j]);
                    }
                }
            }
        }

        return c[0][activity_num+1];
    }
}

int main()
{
    cin >> activity_num;
    for (int i=1; i<=activity_num; i++)
    {
        cin >> activities[i].s >> activities[i].f;
    }

    cout << activity_select_dp() << endl;
    
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值