做了1个小时15分钟,过了样例
#include <bits/stdc++.h>
using namespace std;
struct test
{
int score;
int voucher;
};
vector<unordered_map<int, test>> relation;
//map里面的int表示的是课程的一个代号,表示参加这门考试的先前条件
//test是边上面保存的信息,通过需要的最小分数和可以获得的代金券
unordered_map<int, bool> plan;
//int对应的是课程的代号,bool是表示用户的考试计划里是否计划参加这场考试
//因为是map所以不需要调整范围
unordered_map<int, bool> visit;
//因为在全局里面使用,所以是需要清空的
vector<int> tpath;
vector<int> fpath;
int min_score, max_voucher;
//因为用自然数的下标来代表考试,所以创建一个int的向量
void dfs(int start,int score,int voucher) {
//访问当前课程的所有先修条件,如果这门课是在计划中的
//路径的寻找的终点是那些没有先修要求的课程,但是这样的路径有很多条
if (relation[start].size() == 0 &&
(score < min_score || (score == min_score && voucher > max_voucher))
) {
min_score = score;
max_voucher = voucher;
fpath = tpath;
}
if (relation[start].size() == 0) {
return;
}
for (auto it = relation[start].begin(); it != relation[start].end(); it++) {
if (plan[it->first] == true && visit[it->first] != true) {
//访问先修条件需要把访问设置成真,并且加入路径中
tpath.push_back(it->first);
visit[it->first] = true;
dfs(it->first,score+ it->second.score,voucher+it->second.voucher);//
visit[it->first] = false;//在找到目标的时候回退
tpath.pop_back();
}
//it->first可以访问这门课需要的先修课程,学会使用按钮进行注释,不需要访问 second
}
}
int main()
{
#ifndef ONLINE_JUDGE
FILE* st;
freopen_s(&st, "in.txt", "r", stdin);
#endif // !ONLINE_JUDGE
int n, m;
cin >> n >> m;
relation.resize(n);//节点的编号
//m是选修关系的条数,n是课程的个数
//将总体的课程选修关系录入进去
//录入用户选择的计划
//课程选修的时候每个边都有相关的信息,需要保存下来
int t1, t2, s, d;
for (int i = 0; i < m; i++) {
cin >> t1 >> t2 >> s >> d;
//把对应的小标录入进去,首先需要变量保存用户输入的原始信息
relation[t2][t1] = { s,d };
}
//录入用户自己制定的计划
int k;
//我们需要需要把用户的信息都录入下来来进行统一的计算,因为一门课程的选修课用户可能在后面参加
//上面的信息并不是一个顺序信息,只是告诉用户你选了这么多考试,是否都是可行的,只是一个集合的概念
//因为课程的代码是用数值表示的,所以直接用vector保存就可以了
cin >> k;
vector<int> user_input;
//k是用户输入数据的条数
for (int i = 0; i < k; i++) {
cin >> t1;
user_input.push_back(t1);
plan[t1] = true;
}
//首先判断计划的连续性,判断连续性的话,则是考虑整个考试计划,其中一个考试计划
//的先修课程,必须被选修过,只要先修课程有一门在计划内,这门课就是可行的
//我们现在首先创建一个课程的数据,首先根据用户的信息标注是否选修过
//这个数组是根据课程的下标进行索引的,
bool consistent = true;
//遍历整个计划里面的课程
for (int i = 0; i < k; i++) {
if (!consistent)break;
bool flag = false;
//user_input加上下标可以读取用户的计划
//要考虑一种没有门槛的考试
if (relation[user_input[i]].size() == 0) {
flag = true;
}
else {
for (auto it = relation[user_input[i]].begin(); it != relation[user_input[i]].end(); it++) {
//it->first可以访问这门课需要的先修课程,学会使用按钮进行注释,不需要访问 second
if (plan[it->first] == true) {
flag = true;
break;
}
}
}
//如果flag的是假的,说明这门课不连续,所以说这个整个计划是不合理的
if (!flag) {
consistent = false;
}
//访问某一个门考试的所有先修条件,
//如果检测一门课出来是假的,后续也不用判断了
}
//题目里面只需要检测一个计划
//输出结果
if (consistent) {
cout << "Okay." << endl;
//在可行的情况里面对计划分成两种讨论
//我们需要对计划里面的每个考试进行计划,如果不是直接选的考试,需要给出参加的路径
//而且参加的路径需要注意,不仅仅是没有访问过的,还要考虑遍历的时候不能选择那些没有在计划内的考试
for (int i = 0; i < user_input.size(); i++) {
if (relation[user_input[i]].size() == 0) {
printf("You may take test %d directly.\n", user_input[i]);
}
else {
//生成路径的话需要用dfs进行遍历,dfs遍历的时候的时候需要一个起点,起点就是我们当前的考试
//因为生成的路径是需要保存在向量里面的,所以dfs是不需要返回值的
visit.clear();
tpath.clear();
fpath.clear();
min_score = INT_MAX;
max_voucher = INT_MIN;
tpath.push_back(user_input[i]);
dfs(user_input[i],0,0);
//因为这个结果是反向的,所以需要倒序输出最后的路径,要记得倒序的时候下标0是要输出的
cout << fpath[fpath.size() - 1];
for (int i = fpath.size() - 2; i >= 0; i--) {
cout << "->" << fpath[i];
}
cout << endl;
}
}
}
else {
cout << "Impossible.." << endl;
//先处理整个计划不可行的情况,我们把没有先修条件的课打出来,有先修条件的都报错误就可以了
//把用户的整个计划进行遍历,把有错误的条件列出出来
for (int i = 0; i < user_input.size(); i++) {
if (relation[user_input[i]].size() == 0) {
printf("You may take test %d directly.\n", user_input[i]);
}
else {
printf("Error.");
}
}
}
return 0;
}