Luogu P1280.尼克的任务
原题点这里
思路
方法一:动态规划
这是一道动态规划的题目。
步骤主要分 5 5 5 步:
- 状态的定义
- 转移式的推到
- 递推顺序的判定
- 边界的确定
- 结果的输出
下面,我们针对这道题,细细地讲解一下每一个步骤
一、状态的定义
这道题的状态最简单,又直白——就是
f
i
f_i
fi 表示
i
∼
n
i \sim n
i∼n的最大休息时间。
对于状态的定义大家可以多去试一试,不行了就换一种思路,做多了就会有思路,一般的定义所具备的都是至少有一个
f
i
f_i
fi 代表是第
i
i
i 个。
二、转移式的推到
一般情况下,如果状态找对了,那么转移式便会呼之欲出了。
这道题比较难想,运用到分类讨论的思想!
情况1:
如果时间点 i i i 没有任务,那么 f i = f i + 1 + 1 f_i = f_{i + 1} + 1 fi=fi+1+1,即上一个时间段的最大休息时间加上时间点 i i i 的歇息时间。
情况2:
如果时间点 i i i 是某一个任务的开始点,那么 f i = f i + t a s k i , j f_i = f_{i + task_{i, j}} fi=fi+taski,j(其中 t a s k i , j task_{i, j} taski,j 表示某个任务从 i i i 时间点开始,持续时间为 j j j)
综上所述:
f
i
=
{
f
i
+
1
+
1
max
(
f
i
+
t
a
s
k
i
,
j
)
f_i=\left\{\begin{matrix} & f_{i + 1} + 1\\ & \max(f_{i+task_{i,j}}) \end{matrix}\right.
fi={fi+1+1max(fi+taski,j)
三、递推顺序的判定
这一部分就很简单了,对于这道题因为我们发现一定会要么相同,要么由后面的状态所转移。
为了保持无后效性,我们需采用由大到小的顺序枚举
四、边界的确定
这道题大家可以想一想, f n f_n fn的值是多少呢?根据定义,即 n ∼ n n \sim n n∼n的最大休息时间,休息时间就是 0 0 0。所以初始化数组就是0,故我们不需要确定边界!
五、结果的输出
根据我们的状态的定义,不难确定出答案就是 f 1 f_1 f1
代码
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e4 + 10;
int n, k;
int p, t;
vector<int> task[N];
int f[N];
int main()
{
cin >> n >> k;
for (int i = 1; i <= k; i ++)
cin >> p >> t, task[p].push_back(t);
for (int i = n; i >= 1; i --)
if (task[i].size())
for (auto c : task[i])
f[i] = max(f[i], f[i + c]);
else
f[i] = f[i + 1] + 1;
cout << f[1] << endl;
}
方法二:最短路(Dijkstra)
对于每一个任务,可以连接一条 p p p 到 p + t p+t p+t 边长为 t t t 的边,对于那些没有连接的点,我们就将他们与下一个时间点相连,边权为 0 0 0。之后,跑一遍最短路,就是我们要花费的最小时间。注意,最后返回的是 d i s t n + 1 dist_{n+1} distn+1,而不是 d i s t n dist_n distn。因为任务是从 p + t − 1 p+t-1 p+t−1 结束,最后 p + t p+t p+t 可能会超出 n n n,所以输出 d i s t n + 1 dist_{n+1} distn+1。
代码
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 2e4 + 10;
typedef pair<int, int> PII;
int n, k;
int p, t;
int h[N], e[N], ne[N], w[N], idx;
int dist[N], st[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
int Dijkstra()
{
memset(dist, 0x3f, sizeof dist);
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1});
dist[1] = 0;
while (heap.size())
{
auto t = heap.top();
heap.pop();
int u = t.second, dis = t.first;
if (st[u]) continue;
st[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
if (dist[e[i]] > dis + w[i])
{
dist[e[i]] = dis + w[i];
heap.push({dist[e[i]], e[i]});
}
}
return dist[n + 1];
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> k;
for (int i = 1; i <= k; i ++)
cin >> p >> t, add(p, p + t, t);
for (int i = 1; i <= n; i ++)
if (h[i] == -1)
add(i, i + 1, 0);
cout << n - Dijkstra() << endl;
}
最后祝大家早日——