您的位置:首页 > 其它

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

2018-02-08 00:13 260 查看

P1280 尼克的任务

链接:https://www.luogu.org/problemnew/show/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;

}

运行过程:

最后补充:正推也可以做,但感觉实在不太好考虑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: