【PAT甲级】1175 Professional Ability Test

【PAT甲级】1175 Professional Ability Test_第1张图片

【PAT甲级】1175 Professional Ability Test_第2张图片问题思考:

  1. 首先,若所有的计划(plan)中的节点都可达,则输出 Okay否则输出 Impossible。注意:这里的“plan”判断的是整个图(这里是有向图)上的节点,而不只是那K个queries节点。若存在环,则必然在经历一趟拓扑排序之后,还有留存节点未能遍历到,即判断环内有节点不可达。
  2. 其次,每个query对应输出最佳plan,要求S(score )最少的基础上多争取D(voucher)。这里的麻烦在于,如何化成一个单源最短路问题(这里显然可能存在多个 i 节点是没有前置要求的,即“You may take test i directly”的,即“多源”的最短路)?—— 一个技巧性的做法就是,额外设置一个起点(与所有 i 节点直接相连),并求该点到整个图上的节点的单源最短路问题。
  3. 并用一个Last数组在每选出一条最短路边的时候记录上个节点,用来到时候追溯回去,一直追溯到预设的起点。因为该起点和所有的 i 节点直接相连,所以,求所有 i 节点到query节点的最短路中的最短路,等价于求该起点到queiry节点的最短路。
  4. 若先输出Okay,则之后根据Last数组输出each query节点的最短路
  5. 若先输出Impossible,之后只需判断query是否直接可达无需前置要求,否则输出Error。

代码实现:

#include
#include
#include
using namespace std;
int n, m, k, in[1005], in2[1005], f, last[1005];
struct node {
    int v, score, voucher;
    bool operator < (const node &x) const {
        if (score != x.score) return score > x.score;
        return voucher < x.voucher; 
    }
};
struct edge {
    int next, s, d;
};
vector E[1005]; // 邻接表的简洁表示
queue que;
vector> dis(1002, {2e9, -1});
bool circle() {
    vector S;
    while (que.size()) {
        int now = que.front();
        que.pop();
        S.push_back(now);
        for (auto x : E[now]) {
            in2[x.next]--;
            if (!in2[x.next]) que.push(x.next);
        }
    }
    return S.size() == n;
}
void dijkstra() {
    vector vis(1005);
    priority_queue Q;
    Q.push({1002, 0, 0});
    while (Q.size()) {
        node now = Q.top();
        Q.pop();
        if (vis[now.v]) continue;
        vis[now.v] = 1;
        dis[now.v].first = now.score;
        dis[now.v].second = now.voucher;
        for (auto x : E[now.v]) {
            if (vis[x.next]) continue;
            if (dis[x.next].first > dis[now.v].first + x.s || 
            ((dis[x.next].first == dis[now.v].first + x.s) 
            && (dis[x.next].second < dis[now.v].second + x.d))) {
                dis[x.next].first = dis[now.v].first + x.s;
                dis[x.next].second = dis[now.v].second + x.d;
                last[x.next] = now.v;
                Q.push({x.next, dis[x.next].first, dis[x.next].second});
            }
        }
    }
    return ;
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int a, b, s, d;
        scanf("%d%d%d%d", &a, &b, &s, &d);
        // 构建邻接表
        E[a].push_back(edge{b, s, d});
        in[b]++, in2[b]++;
    }
    // 寻找无前置要求的节点
    for (int i = 0; i < n; i++) {
        if (in[i] == 0) {
            E[1002].push_back({i, 0, 0}); // 起点与无前置要求的节点相连
            que.push(i);
        }
    }
    f = circle();
    dijkstra();
    cin >> k;
    if (f) cout << "Okay.\n";
    else cout << "Impossible.\n";
    for (int i = 0, q; i < k; i++) {
        scanf("%d", &q);
        if (!in[q]) cout << "You may take test " << q << " directly.\n";
        else if (!f) cout << "Error.\n";
        else {
            vector path;
            while (q != 1002) {
                path.push_back(q);
                q = last[q];
            }
            for (int j = path.size() - 1; j >= 0; j--) {
                cout << path[j];
                j && cout << "->";
            }
            cout << '\n';
        }
    }
    return 0;
}

知识点:小于号重载

  • 这里需要注意,重载小于号的方向,下面是一个实验样例。
#include
#include
#include
struct node {
    int v, score, voucher;
    bool operator < (const node &x) const {
        if (score != x.score) return score > x.score;
        return voucher < x.voucher; 
    }
};
int main() {
    priority_queue que;
    que.push(node{0, 1, 2});
    que.push(node{1, 1, 3});
    que.push(node{2, 2, 2});
    while (que.size()) {
        node now = que.top(); que.pop();
        cout << "now : " << now.v << " " << now.score << " " << now.voucher << endl;
    }
    return 0;
}
  • 运行结果: 
now : 1 1 3
now : 0 1 2
now : 2 2 2

可见如果想要让自定义结构满足按其某个property从小到大排序,就得这么写:

bool operator < (const node &x) const {
    return this.property > x.property
}

你可能感兴趣的:(代码刷题,算法)