问题背景
西西艾弗岛荒野求生大赛还有 n 天开幕!
问题描述
为了在大赛中取得好成绩,顿顿准备在 n 天时间内完成“短跑”、“高中物理”以及“核裂变技术”等总共 m 项科目的加强训练。其中第 i 项(1≤i≤m)科目编号为 i,也可简称为科目 i。已知科目 i 耗时 天,即如果从第 a 天开始训练科目 i,那么第 a+−1 天就是该项训练的最后一天。
大部分科目的训练可以同时进行,即顿顿在同一天内可以同时进行多项科目的训练,但部分科目之间也存在着依赖关系。如果科目 i 依赖科目 j,那么只能在后者训练结束后,科目 i 才能开始训练。具体来说,如果科目 j 从第 a 天训练到第 a+−1 天,那么科目 i 最早只能从第 a+ 天开始训练。还好,顿顿需要训练的 m 项科目依赖关系并不复杂,每项科目最多只依赖一项别的科目,且满足依赖科目的编号小于自己。那些没有任何依赖的科目,则可以从第 1 天就开始训练。
对于每一项科目,试计算:
1)最早开始时间:该科目最早可以于哪一天开始训练?
2)最晚开始时间:在不耽误参赛的前提下(n 天内完成所有训练),该科目最晚可以从哪一天开始训练?
n 天内完成所有训练,即每一项科目训练的最后一天都要满足 ≤n。需要注意,顿顿如果不能在 n 天内完成全部 m 项科目的训练,就无法参加大赛。这种情况下也就不需要再计算“最晚开始时间”了。
输入格式
从标准输入读入数据。
输入共三行。
输入的第一行包含空格分隔的两个正整数 n 和 m,分别表示距离大赛开幕的天数和训练科目的数量。
输入的第二行包含空格分隔的 m 个整数,其中第 i 个(1≤i≤m)整数 表示科目 i 依赖的科目编号,满足 0≤<i;=0 表示科目 i 无依赖。
输入的第三行包含空格分隔的 m 个正整数,其中第 i 个(1≤i≤m)数 表示训练科目 i 所需天数,满足 1≤≤n。
输出格式
输出到标准输出中。
输出共一行或两行。
输出的第一行包含空格分隔的 m 个正整数,依次表示每项科目的最早开始时间。
如果顿顿可以在 n 天内完成全部 m 项科目的训练,则继续输出第二行,否则输出到此为止。
输出的第二行包含空格分隔的 m 个正整数,依次表示每项科目的最晚开始时间。
样例 1 输入
10 5
0 0 0 0 0
1 2 3 2 10
样例 1 输出
10 5
0 0 0 0 0
1 2 3 2 10
样例 1 说明
五项科目间没有依赖关系,都可以从第 1 天就开始训练。
10 天时间恰好可以完成所有科目的训练。其中科目 1 耗时仅 1 天,所以最晚可以拖延到第 10 天再开始训练;而科目 5 耗时 10 天,必须从第 1 天就开始训练。
样例 2 输入
10 7
0 1 0 3 2 3 0
2 1 6 3 10 4 3
样例 2 输出
1 3 1 7 4 7 1
样例 2 说明
七项科目间的依赖关系如图所示,其中仅科目 5 无法在 10 天内完成训练。
具体来说,科目 5 依赖科目 2、科目 2 又依赖于科目 1,因此科目 5 最早可以从第 4 天开始训练。
样例 3 输入
10 5
0 1 2 3 4
10 10 10 10 10
样例 3 输出
1 11 21 31 41
子任务
70% 的测试数据满足:顿顿无法在 n 天内完成全部 m 项科目的训练,此时仅需输出一行“最早开始时间”;
全部的测试数据满足 0<n≤365 且 0<m≤100。
思路:思路都写在了代码注释里!!!
代码实现:
#include<iostream>
#include<map>
#include<vector>
using namespace std;
int main() {
int n, m;
map<int, int>maps; //有序哈希表存储第i项科目依赖的科目
cin >> n >> m;
vector<int>days; //存储训练第i项科目需要天数
vector<int>prime; //存储最早开始时间
vector<int>last(101,n); //存储最晚开始时间,默认假设m(数据格式m最大值为101)项科目都是最晚(n天)开始的。长度为101,
bool flag = true; //标记是否能在n天内完成全部m项科目的训练
for (int i = 0; i < m; ++i) { //科目编号为key,从0开始。依赖科目编号为value,value为-1表示没有依赖
int j;
cin >> j;
maps[i] = j - 1;
}
for (int i = 0; i < m; ++i) { //训练第i项科目需要的天数
int j;
cin >> j;
days.push_back(j);
}
for (int i = 0; i < m; ++i) { //从前往后记录每项科目的最早开始时间
if (maps[i] == -1) //没有依赖的项目最早开始时间就是第一天
prime.push_back(1);
else { //有依赖的项目最早开始时间为依赖项目的最早开始时间+完成依赖项目需要的时间
int day = prime[maps[i]] + days[maps[i]];
prime.push_back(day);
if (day + days[i] > n) //当前项目的最早开始时间+它完成时间超过n天,表明不能在比赛开始前完成训练,标记标志位为false
flag = false;
}
}
for (int i = 0; i < prime.size(); ++i) //输出最早开始时间
cout << prime[i] << " ";
cout << endl;
if (flag) { //比赛开始前可以完成项目训练
for (int i = m - 1; i >= 0; --i) { //从后往前记录最晚开始时间
last[i] -= days[i]; //最晚也要完成当前项目的训练时间,用暂定最晚开始时间减去当前项目的训练时间
int day = last[i]; //定义变量day记录当前项目最晚开始时间
/*
!!!!!!!!!!!重点!!!!!!!!!!!
由于被依赖项目的最晚开始时间受依赖它的项目的最晚开始时间影响
因此若一个项目有依赖项目(依赖项目编号一定在它前面),
则其依赖项目的最晚开始时间至少和它的最晚开始时间一样。
所以一个项目有依赖,更新被依赖项目最晚开始时间((暂定,不考虑自身的训练时间))和它的一样。
此时会遇到一个问题:有两个项目依赖同一个项目。
解决办法:首先这两个项目之间不可能存在依赖关系,它俩可以并行训练。
被依赖项目的最晚开始时间(暂定,不考虑自身的训练时间)此时取决于这两个项目哪一个的训练时间长,
也就是让被赖项目的最晚开始时间(暂定,不考虑自身的训练时间)尽可能的小。
举个例子
假设被依赖项目x完成自身训练需要1天 刚开始默认所有项目最晚开始时间为20
有两个项目a,b依赖x,b的编号大于a。a,b两个项目的完成自身训练需要4、3天,则最晚开始时间为16,17天。
从后往前遍历,x最先受b影响,17<20,x最晚开始时间为17,接着又受a影响,16<17,x最晚开始时间为16。
*/
if (maps[i] != -1) {
last[maps[i]] = day > last[maps[i]] ? last[maps[i]] : day; //maps[i]表示被依赖项目编号。last[maps[i]]表示被依赖项目最晚开始时间
}
}
for (int i = 0; i < m; i++) //输出最晚开始时间,由于上一步相减操作中没有考虑多减一天的影响,输出的时候加上这一天
{
cout << last[i] + 1 << " ";
}
cout << endl;
}
}