HDU-2037 今年暑假不AC 贪心
题目链接 :https://vjudge.net/problem/HDU-2037
题意:
活动安排问题,要求活动之间不能有重叠,问最多能够安排多少活动
分析:
这是经典的活动安排问题
首先可能想到是贪心问题或者动态规划,直觉想到可能是贪心问题。可以想到的可能有三种安排策略:
① 先开始的活动先安排
② 时间短的活动先安排
③ 早结束的活动先安排
第一种策略不正确,举例如下:
a1=<0, 20>, a2=<2, 5>, a3=<8, 15>,如果用这种策略则最多只能安排a1一个活动,但事实上可以安排a2,a3两个活动。
第二种策略也不正确,举例如下:
a1=<0, 8>, a2=<7, 9>, a3=<8, 15>,如果用这种策略最多只能安排a2一个活动,但事实上可以安排a1 ,a3 两个活动。
第三种策略我觉得可以这么思考,假设前k个活动的安排是最优的,那么它一定是结束时间最早的一种安排方法(这样在后面的安排中才会有更多的时间,安排更多的活动),将规模减小至只有一个,即是早结束的活动先安排。下面给出策略正确性的证明:
证明:(对算法步数进行归纳)
(1) 将活动集合按照截止时间递增顺序排列,得到序列S。
(2) 算法第一步选择S中的活动1,设算法最终得到问题最优解为A={i1, i2, …, ij},则i1=1;(算法第一步正确),若i1≠1,用1替换i1,可得到A’=(A-{i1})∪{1},集合A’与A中活动个数相同,且1比i1结束的更早,因此与i2, i3, …, ij相容,于是A’也是问题的一个最优解。
(3) 假设算法前k步正确,令i1=1, i2, …, ik是前k步顺序选择的活动,则原为题最优解可表示为A={i1=1, i2, …, ik}∪B,令S’是S中剩余的与i1, i2, …, ik相容的活动,即S’={j | sj≥fik, j∈S},则B是S’的一个最优解。若不然,假设S’有最优解B’,且|B’| > |B|,那么用B’替换B可以得到的解={i1=1, i2, …, ik} ∪ B’将比A的活动更多,与A为最优解矛盾。
(4) 根据第(2)步,子问题S’存在最优解B*={ik+1, …},则A’={i1=1, i2, …, ik} ∪B*={i1=1, i2, …, ik, ik+1} ∪(B*-{ik+1})为原问题一个最优解,且恰好包含了前k+1步选择的活动。
策略确定之后,就是实现的方法,我的想法是通过自定义结构体并定义优先级(结束时间早的优先级高),再通过优先队列存储节目,最后pop一遍判断可以观看的节目数量。
代码如下:
#include <iostream> #include <queue> using namespace std; struct show { int s; int f; bool operator < (const show& s2) const{ if(this->f > s2.f) { return 1; } else if(this->f == s2.f){ return this->s < s2.s; } else return 0; } }; int main(void) { int n; while(scanf("%d", &n) == 1 && n != 0) { priority_queue <show> q; int ans = 0; show now; now.s = 0, now.f = 0; while(n--) { show S; scanf("%d%d",&S.s, &S.f); q.push(S); } // while(!q.empty()) { // printf("%d - %d\n", q.top().s, q.top().f); // q.pop(); // } while(!q.empty()) { show S = q.top(); q.pop(); if(S.s >= now.f) { ans++; now = S; } } cout << ans << endl; } }