搞清楚了就很简单的dp题!
思路
设dp[i]表示i ~ n的最大空闲时间
状态转移方程:
(空闲) dp[i] = dp[i + 1] + 1;
(有工作)dp[i] = max(dp[i], dp[i + s[num++].time);
(在 i 时刻的开始工作的此工作需要持续的时间为 s[num++].time)
所以我们需要一个结构体来存储开始的时间和工作持续的时间
struct Node
{
int begin, time;
}s[N];
因为后面的开始时间会影响前面的休息时间,有多种可能的选择,所以我们不从前面枚举,而是从最后往前面枚举休息时间。
为了让开始时间放到一起,所以我们要排序一下,又因为我们要从最后开始枚举起,所以我们要按降序来排序
bool cmp (Node x, Node y)
{
return x.begin > y.begin;
}
sort(s + 1, s + k + 1, cmp);
哼哼,紧接着就开始举例子了!(因为语言根本不会说)
我们看排序后的样例!
这就是排好序的开始时间和工作要持续的时间。然后我们从n ~ 1开始枚举每个 i 的空闲时间,分两个状态转移方程(在上面)
dp n
1 15 dp[15] = dp[15 + 1] + 1 = dp[16] + 1 = 0 + 1 = 1;
(n = 15的时候没有需要开始工作的时间, 所以空闲时间直接加1)
2 14 dp[14] = dp[14 + 1] + 1;
3 13 dp[13] = dp[13 + 1] + 1;
4 12 dp[12] = dp[12 + 1] + 1;
0 11 dp[11] = max (dp[11 + 5], dp[11]);
dp[16] = dp[11 + s[11].time] <-(s[11].time == 5)
dp[11] = dp[16] = 0;
(n = 11有工作,持续时间为 5 ,所以在nz在11~15休息时间为0)
1 10 dp[10] = dp[10 + 1] + 1;
2 9 dp[9] = dp[9 + 1] + 1;
3 8 (j = 1)dp[8] = max (dp[8 + 5], dp[8]);
(j = 2)dp[8] = max (dp[8 + 1], dp[8]);
dp[8] = max((j = 1)dp[8], (j = 2)dp[8]);
dp[8] = (j = 1)dp[8] = dp[13] = 3;
(n = 8有两份工作,所以比较这两份工作哪份休息时间多就选哪份)
4 7 dp[7] = dp[7 + 1] + 1;
5 6 dp[6] = dp[6 + 1] + 1;
6 5 dp[5] = dp[5 + 1] + 1;
1 4 dp[4] = max (dp[4 + 11], dp[4]) = dp[15] = 1;
2 3 dp[3] = dp[3 + 1] + 1;
3 2 dp[2] = dp[2 + 1] + 1;
4 1 dp[1] = dp[1 + 1] + 1;
当当~就这样dp[1]就是1 - n 的最大空闲时间啦! 正解得出!
下面就直接放代码咯~
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1e5 + 50;
int n, k, dp[N], num[N];
struct Node
{
int begin, time;
friend bool operator < (Node x, Node y)
{
return x.begin > y.begin;
}
}s[N];
int main()
{
cin >> n >> k;
for(int i = 1; i <= k; i++)
{
cin >> s[i].begin >> s[i].time;
num[s[i].begin]++;
}
sort(s + 1, s + k + 1);
int p = 1;
for(int i = n; i >= 1; i--)
{
if(num[i] == 0)
{
dp[i] = dp[i + 1] + 1;
}
else
{
for(int j = 1; j <= num[i]; j++)
{
dp[i] = max (dp[i + s[p++].time], dp[i]);
}
}
}
cout << dp[1] << endl;
return 0;
}