洛谷题单指南-图的基本应用-P1113 杂务
原题链接:https://www.luogu.com.cn/problem/P1113
题意解读:要计算所有任务完成的最早时间,其实取决于最晚完成的那一个任务,计算每一个任务的完成时间,找最大值即可。
解题思路:
下面介绍两种做法:递推法、拓扑排序法
1、递推法
由于杂务k (k>1) 的准备工作只可能在杂务1 至 k−1 中,因此可以通过前面的杂务完成所需时间推出后面的杂务完成所需时间。
由于杂务可能有很多前序任务,每个杂务能完成的时间取决于前序任务中耗时最久的一个。
由于数据是按照工作序号递增给出的,可以在读取数据的时候计算完成每个任务所需的最大时间。
完成所有任务的最少时间就是所需时间最多的那一个任务耗时。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
int t[N]; //完成每个任务的耗时
int n, k, len, x, ans;
int main()
{
cin >> n;
while(n--)
{
cin >> k >> len;
int maxx = 0;
while(cin >> x && x != 0)
{
maxx = max(maxx, t[x]); //找到前序任务耗时最多的
}
t[k] = maxx + len; //更新任务k的耗时 = 前序任务最多时间 + 自身耗时
ans = max(ans, t[k]);
}
cout << ans << endl;
return 0;
}
2、拓扑排序法
如果通过搜索来计算每个节点任务完成的时间,传统的DFS、BFS都无法保证所有前序任务都搜索之后再搜索后序任务,而拓扑排序恰好具备此特性,
因为在拓扑序中,某个节点要加入队列的前提是其入度为0,也就是所有前置节点都已经访问过了,这样就能根据前置节点任务完成的最大时间来更新此节点的完成时间,具体逻辑参考代码。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
vector<int> g[N]; //领接表建图
int l[N]; //完成每个任务自身所需时间
queue<int> q;
int t[N]; //每个任务完成的最早时间,答案是最大的那一个
int in[N]; //每个节点的入度
int n, k, len, x, ans;
//拓补排序法
void bfs()
{
for(int i = 1; i <= n; i++)
{
if(in[i] == 0)
{
q.push(i);
t[i] = l[i]; //节点i完成时间是l[i]
}
}
while(q.size())
{
int u = q.front(); q.pop();
for(int i = 0; i < g[u].size(); i++)
{
int v = g[u][i]; //遍历每个u指向的节点v
if(--in[v] == 0) q.push(v); //入度减为0的节点加入队列
t[v] = max(t[v], t[u] + l[v]); //更新v完成的时间,如果有多个指向v,就更新多次,取最大值
ans = max(ans, t[v]);
}
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> k >> len;
l[k] = len; //保存每个节点的耗时
while(cin >> x && x != 0)
{
g[x].push_back(k); //建图
in[k]++; //k的入度+1
}
}
bfs();
cout << ans;
return 0;
}