题意概览:
给你几个工作区间,每个区间都有一定的开始和结束时间,每次工作后还必须休息一段时间才可以继续工作,问题是求解在这段时间内最大的工作效益。
解题思路:
不难想到,这是一个类似于背包问题的动态规划问题。但有一个要注意到的地方,就是每次工作后都要休息一个固定的时间段,看起来非常让人疑惑,感觉这是一个很Interesting的题目,但想通了后却又觉得很Naive,因为每个工作完成都要等待这个事间,因此完全可以把它加到每个工作的结束时间里去,也就是结束完以后等待,对下一个要进行的任务来讲,效果和前一个结束时间变晚了是一致的。接下来,只需要对问题的本质进行分析即可。
首先,对每个工作来说,都有选或者不选这两种选择,对于这类问题,我解题的思路一般就是十个字:从后往前想,从前往后写。从后往前想是因为借助这种思路可以把一个大的问题往小的问题划分,也就是让难于求解的问题变得简单。而从前往后写则是借住了动态规划算法的优越性,可以从最小的子问题开始求,保证此后的每一个状态都是一个最优解,同时也不用大量调用递归函数,导致巨大的开销。从而,对于这道题来说,我们首先要对这些工作进行一个排序,以开始时间为主序,结束时间为次序。以此来作为选择的先决条件,即在这个工作是否选择要依据在它开始时间以前结束的所有工作里寻找,所以DP【i】的意义就是前i个任务且最后一个任务是i必选的情况下所能取得的最大收益,因而状态转移方程可以写为:
dp[i]=max(dp[i],dp[j]+m[i].value) , if(m[j].end<=m[i].start)
也就是说,在i开始前所有能完成的任务状态里面找到最大的dp[i],这样dp[i]始终确保了选到第i个任务的情况下的最大收益,所以扫过一遍后只需要在所有的dp[i]中找到最大值即为问题的最优解。
题解代码
#include<iostream>
#include<algorithm>
using namespace std;
const int size = 1000000+10;
int dp[size];
typedef struct{
int start,end,value;
}milk;
milk m[size];
int cmp(milk a, milk b){
if(a.start==b.start)
return a.end>b.end;
return a.start<b.start;
}
int Max(int a,int b){
return a>b?a:b;
}
int main(){
int N,M,R;
cin>>N>>M>>R;
for(int i=0;i<M;i++){
cin>>m[i].start>>m[i].end>>m[i].value;
m[i].end=m[i].end+R;
}
sort(m,m+M,cmp);
for(int i=0;i<M;i++){
dp[i]=m[i].value;
for(int j=0;j<i;j++){
if(m[i].start>=m[j].end)
dp[i]=Max(dp[i],dp[j]+m[i].value);
}
}
int res=0;
for(int i=0;i<M;i++)
if(dp[i]>res)res=dp[i];
cout<<res<<endl;
return 0;
}