动态规划:洛谷 P1280 尼克的任务

洛谷 P1280 尼克的任务

 

 

 

 

    这是洛谷的一题绿题,考的是动态规划。我们先想状态转移方程,这是个线性的dp,那么可以考虑每一个时间,dp[]就代表0-此时间内的最大闲暇时间,但发现最大闲暇时间,前面的选择会对后面的选择产生影响,有后效性,所以不妨倒着来,从最后一个时间一直递推到第一个,后面选择的任务对前面任务的选择并不会产生影响的。所以我们遍历每一个时间,状态转移方程:如果这个点没有work,那么dp[i]=dp[i+1]+1就是上一个空闲时间+1,如果有,就开一个循环,遍历这个时间点的所有工作worktime[j],dp[i]=max(dp[i+worktime[j]]);

    上代码:

 1 //洛谷 P1280 尼克的任务
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 int dp[10005], num[10005], n, k;//num数组存的是这个时间点有几个任务开始
 7 //dp数组存的是从n分钟到这个第i时间的最长休闲时间
 8 struct worktime
 9 {
10     int b;
11     int l;
12 }work[10005];//存放工作开始和持续时间的结构体
13 bool cmp(worktime a, worktime c)
14 {
15     return a.b > c.b;//从大到小排序一下,因为是逆向 从时间大到时间小去DP
16 }
17 int main()
18 {
19 
20     cin >> n >> k;
21     for (int i = 1; i <= k; ++i)
22     {
23         cin >> work[i].b >> work[i].l;
24         num[work[i].b]++;
25     }
26     sort(work + 1, work + 1 + k, cmp);//从小到大排序一下
27     int x = 1;//这是一个标志,因为下面有任务就要遍历一下这个时间点的所有work,寻找最优解
28     //我们干脆引入一个x就是work里面的下标,因为work已经从大到小排序过,所以我们每次只要看work[x],x++,这样每次判断的work[x]会刚好,可以自己举例验证
29     for (int i = n; i >= 1; --i)
30     {
31         if (!num[i])//如果这个时间没有work 那么他的休闲时间就等于前一个休闲时间+1分钟
32             dp[i] = dp[i + 1] + 1;
33         else//否则就开始循环 找最优解
34         {
35             for (int j = 1; j <= num[i]; ++j)//循环num[i]次
36             {
37                 dp[i] = max(dp[i], dp[i + work[x].l]);
38                 x++;
39                 //如果只有一个任务,那么dp[i]一定等于0,这样也一定会接这个任务,
40                 //保证了符合题目,有一个任务必须做,有两个以上就max比较,因为i到i+work[x].l的时间在做任务
41                 //所以i时间的最大休息时间等于i+workx.l;
42                 //记得++x
43             }
44         }
45 
46     }
47 
48     cout << dp[1];//dp[1]就是最终答案
49     return 0;
50 
51 }

 

完美通过:

 

 时间复杂度也只是o(n+k),顺利通过。

posted @ 2022-04-12 09:17  朱朱成  阅读(113)  评论(0编辑  收藏  举报