问题阐述
给定若干个工作的开始时间、结束时间和权重(可以理解成重要程度),求出能完成的最大的工作权重(尽可能地完成更重要的工作),当然必须满足各个工作相容。如以下三个工作:
开始时间 结束时间 权重
工作1 0 3 4
工作2 5 6 5
工作3 2 8 10
由不带权重的区间调度方法,依次结束时间最早且相容的工作,这里就选出{1, 2},能实现的最大权重是4+5=9。而显而易见,选择{3}权重可达到10,因此最早结束时间的贪心策略在带权重的区间调度问题里已不适用。
分析
给出上图所示8个工作的开始结束时间,按结束时间升序排序(区间调度问题一般都会先按结束时间升序排序)。 数据说明如下:
- 工作数目n
- 声明结构体数组存储工作的开始、结束时间和权值
struct JOB{
int s, e, v;
JOB(int s=0, int e=0, int v=0):s(s), e(e), v(v){}
}job[maxn];
- dp[n],dp[i]表示对前1个工作考虑结束后所能达到的最大权值
- frt[n],frt[i]表示序列中前一与之相容的最大工作编号。如frt[8]=5,frt[6]=2
考虑状态转移方程。对于每个工作i,比较dp[i-1]和v[i]+dp[frt[i]],取较大者为dp[i]
代码实现
#include
#include
#include
using namespace std;
const int maxn = 205;
struct JOB{
int s, e, v, index;
JOB(int s=0, int e=0, int v=0, int index=0):s(s), e(e), v(v), index(index){}
}job[maxn];
int frt[maxn], dp[maxn];
bool cmpe(JOB a, JOB b){ //定义按结束时间升序排序的排序规则
return a.e> n;
//JOB a=(1,2,3);
memset(frt, 0, sizeof(frt));
memset(dp, 0, sizeof(dp));
for(int i=1;i<=n;i++) cin >> job[i].s >> job[i].e >> job[i].v;
//按结束时间升序排序,并为元素编号
sort(job, job+n, cmpe);
for(int i=1; i<=n; i++) job[i].index=i;
//求frt[]数组
//sort(job, job+n, cmps); //按开始时间升序排列
for(int i=n;i>0;i--){
for(int j=n-1;j>0;j--){
if(job[j].e<=job[i].s) {frt[i]=job[j].index; break;}
}
}
//sort(job, job+n, cmpe);
for(int i=1;i<=n;i++){
dp[i] = max(dp[i-1], dp[frt[i]]+job[i].v);
}
cout << dp[n];
return 0;
}
老师的ppt里有一句话,“按开始时间排序后,计算frt数组的复杂度为O(n)”,但我没想出来怎么个O(n)法,上面代码还是用的简单的遍历,希望看到这里的朋友能不吝赐教>_<
Tips
- 自定义sort函数的排序规则
- 在O(nlogn)时间内计算frt数组的技巧(我不知道)