PAT(甲级)2020年秋季考试 7-4 Professional Ability Test 经验分享与心路历程

 做了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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Exodus&Focus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值