POJ 3616 Milking Time

本文介绍了一种通过动态规划解决带有特定约束的工作区间问题的方法。该问题要求在考虑到每个工作完成后必须休息一段时间的情况下,最大化工作效益。文章详细阐述了解题思路,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意概览:
给你几个工作区间,每个区间都有一定的开始和结束时间,每次工作后还必须休息一段时间才可以继续工作,问题是求解在这段时间内最大的工作效益。
解题思路:
不难想到,这是一个类似于背包问题的动态规划问题。但有一个要注意到的地方,就是每次工作后都要休息一个固定的时间段,看起来非常让人疑惑,感觉这是一个很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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值