1280 尼克的任务

难度:普及+/提高

题目类型:动规

提交次数:1

涉及知识:线性动规

题目描述

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

尼克的一个工作日为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 #include<iostream>
 2 #include<algorithm>
 3 #include<vector> 
 4 using namespace std;
 5 int n, k;
 6 struct task{
 7     int start;
 8     int last;
 9     int end;
10 };
11 bool com(task a, task b){
12     return a.start<b.start;
13 }
14 int d[10010];//表示以第i个任务结尾的工作序列的最小消耗时间 
15 int b[10010];//表示第i分钟开始的任务个数 
16 int s[10010];//表示第1到i分钟的任务个数 
17 int main(){
18     cin>>n>>k;
19     int i, j;
20     vector<task>a(k+1);
21     for(i = 1; i <= k; i++){
22         int start, last, end;
23         cin>>start>>last;
24         end = start+last-1;
25         a[i].start = start;
26         a[i].end = end;
27         a[i].last = last;
28         b[start]++; 
29     }
30     sort(a.begin()+1, a.end(), com);
31     
32     for(i = 1; i <= n; i++)
33         s[i] = s[i-1]+b[i];
34     
35     for(i = 1; i <=k; i++){
36         d[i] = 10010;
37         for(j = 0; j < i; j++){
38             if(a[j].end<a[i].start&&s[a[j].end]==s[a[i].start-1])
39                 d[i] = min(d[i], d[j]+a[i].last);
40         }
41     }
42     int ans = 10010;
43     for(i = 1; i <=k; i++){
44         if(s[a[i].end]==s[n])
45             ans = min(ans, d[i]);
46     }
47     cout<<n-ans<<endl;
48         
49     return 0;
50 }

备注:

有思路后还是很清晰的一道题。最大空闲时间等价于求最少工作消耗时间。所以子问题是“以第i个任务为结尾的最少工作消耗时间”,在我的代码里用d[i]表示。状态怎样转移呢?引号内内容摘自洛谷题解,感觉说的非常清楚:

“把任务按照开始时间排序,以确保如果某一时刻无其他任务必须做一个。

f[i]=f[j]+cost(i) 仅当end(j)<start(i) 且 在区间(end(j),start(i))中无其他任务。

使用cnt(i)表示从1-i时刻有几个任务,如果cnt(a)==cnt(b)则说明a,b间无任务。”

一开始我居然不知道原问题的解是什么。。其实很简单啊,最后一个任务到工作时间结束之间没有其他任务开始的话,这就是一个完整的可行解。跟状态转移是一样的。

在我的代码里s数组就是回答中的cnt数组。我觉得cnt数组的设置和处理是十分巧妙的(已标黄)。它的递推过程也体现了一点动规的思想。

另外很巧妙的一点就是已标黄的“j=0”,这样一个小技巧就完美解决了在之前没有任务的情况下,最短消耗时间就是这个工作的消耗时间。

最后吐槽一点,我居然不小心在if后加了分号,并且因为这个问题查了一个小时orz

原文地址:https://www.cnblogs.com/fangziyuan/p/5932314.html