寒假训练2018.2.7训练日志------dp学习(二)尼克的任务

P1280 尼克的任务

题目描述

尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。

尼克的一个工作日为N分钟,从第一分钟开始到第N分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第P分钟开始,持续时间为T分钟,则该任务将在第P+T-1分钟结束。

写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。

输入输出格式

输入格式:

输入数据第一行含两个用空格隔开的整数N和K(1≤N≤10000,1≤K≤10000),N表示尼克的工作时间,单位为分钟,K表示任务总数。

接下来共有K行,每一行有两个用空格隔开的整数P和T,表示该任务从第P分钟开始,持续时间为T分钟,其中1≤P≤N,1≤P+T-1≤N。

输出格式:

输出文件仅一行,包含一个整数,表示尼克可能获得的最大空暇时间。

输入输出样例

输入样例#1:  复制
15 6
1 2
1 6
4 11
8 5
8 1
11 5
输出样例#1:  复制
4








--------------------------------------------------------分割线------------------------------------------------------------------

参考了好多题解,好多文章。。。。。。。。。。。。。。。

一、先来看一下dp问题一般的解题步骤: https://www.cnblogs.com/TX980502/p/7705061.html

捡几个重要的说(对于本题)
①确定状态:这里的状态即f[ ]数组的下标,像前面提到的01背包滚动数组解法中状态是不同的体积(或质量)。
②用状态转移方程求解不同状态的值:状态转移方程即状态”i“→状态”i+1“(或”i-1“,具体看”i“枚举顺序)的递推式。

  首先对本题来说 (各事件已经按照开始时间排好序),我们选择时间来作为状态,而不是事件(课本上有一句话”阶段的划分一般是根据时间和空间的自然特征来划分的“还是挺有道理的)。
 所以下面我们要做的就是求解不同状态的值。
  i顺序增加时:不妨我们先不考虑边界值(f[0]=0),先写出一个对于大多数状态来说都适合的方程。又因为考虑到在每次状态
转移时(即求解当前状态时)我们有两种决策:①该状态下有工作要做②该状态下无工作要做,所以我们得到如下递推式,
①无工作:f[i]=f[i-1]+1 ②有工作:f[i]=f[i-1]+(下一事件开始时间-同时刻开始的所有工作中持续时间最小的那份工作的结束时间)
,而此时,我们发现括号中内容,即同时刻开始的几个事件中休息时间最长的时间是未知的,所以不好实现。
因此我们将i改为逆序:①无工作时:f[i]=f[i+1]+1 ②有工作:f[i]=f[ i+当前工作的持续时间 ]。
看下大佬的解释(自己水平还是不够):
@ ylsoi 2017-07-17 08:32  回复

让我来讲一下这个题目为什么要倒着推!!!这一题可是难死我了,个人认为此题最最最重要的就是看清楚题意,也就是此时有任务(不在工作状态)就必须选,有很多个就选一个,所以当这个时间只有一个状态开始的时候,我们是没有任何的话说的,但是如果有很多的任务同时开始,我们要选取最优的那个取决这个任务结束后的情况,但是任务结束后的情况我们之前有没有推过,所以我们要倒着推。


ac代码:

参考题解:http://blog.csdn.net/clove_unique/article/details/50448940

   http://blog.csdn.net/ly59782/article/details/52006319

   http://blog.csdn.net/sdfzyhx/article/details/52027745

#include<bits/stdc++.h>
using namespace std;
struct work
{
    int x,y;
}a[10001];
bool complare(work p,work q)
{
    return p.x<q.x;
}
int f[10001];
int main()
{
    int n,k;cin>>n>>k;
    for(int i=1;i<=k;++i) cin>>a[i].x>>a[i].y;
    sort(a+1,a+1+k,complare);
    int j=k;

下面是关键:

for(int i=n;i>=1;--i)
    {
        cout<<"i="<<i<<"  ";
        if(i!=a[j].x){f[i]=f[i+1]+1;cout<<"f["<<i<<"]="<<f[i]<<"  ";}
        else
        {
            while(i==a[j].x)
            {
                f[i]=max(f[i],f[i+a[j].y]); //此处f[i]并非由f[i+1]推出,而是在前面的所有结果中作选择。从这里可以思考一下贪心与dp的区别。或者说dp是怎么得到最优解的。 
                j--;
                cout<<"f["<<i<<"]="<<f[i]<<" ";
            }
        }
        cout<<endl;
    }
    cout<<f[1];
    return 0;

}


运行过程:

最后补充:正推也可以做,但感觉实在不太好考虑。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值