PAT(甲級)2020年秋季考試 7-4 Professional Ability Test

7-4 Professional Ability Test (30分)

Professional Ability Test (PAT) consists of several series of subject tests. Each test is divided into several levels. Level A is a prerequisite (前置要求) of Level B if one must pass Level A with a score no less than S in order to be qualified to take Level B. At the mean time, one who passes Level A with a score no less than S will receive a voucher(代金券)of D yuans (Chinese dollar) for taking Level B.算法

At the moment, this PAT is only in design and hence people would make up different plans. A plan is NOT consistent if there exists some test T so that T is a prerequisite of itself. Your job is to test each plan and tell if it is a consistent one, and at the mean time, find the easiest way (with minimum total S) to obtain the certificate of any subject test. If the easiest way is not unique, find the one that one can win the maximum total value of vouchers.數組

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers N (≤1000) and M, being the total numbers of tests and prerequisite relations, respectively. Then M lines follow, each describes a prerequisite relation in the following format:less

T1 T2 S D

where T1 and T2 are the indices (from 0 to N−1) of the two distinct tests; S is the minimum score (in the range (0, 100]) required to pass T1 in order to be qualified to take T2; and D is the value of the voucher (in the range (0, 500]) one can receive if one passes T1 with a score no less than S and plan to take T2. It is guaranteed that at most one pair of S and D are defined for a prerequisite relation.ide

Then another positive integer K (≤N) is given, followed by K queries of tests. All the numbers in a line are separated by spaces.測試

Output Specification:

Print in the first line Okay. if the whole plan is consistent, or Impossible. if not.優化

If the plan is consistent, for each query of test T, print in a line the easiest way to obtain the certificate of this test, in the format:ui

T0->T1->...->T

However, if T is the first level of some subject test (with no prerequisite), print You may take test T directly. instead.this

If the plan is impossible, for each query of test T, check if one can take it directly or not. If the answer is yes, print in a line You may take test T directly.; or print Error. instead.spa

Sample Input 1:

8 15
0 1 50 50
1 2 20 20
3 4 90 90
3 7 90 80
4 5 20 20
7 5 10 10
5 6 10 10
0 4 80 60
3 1 50 45
1 4 30 20
1 5 50 20
2 4 10 10
7 2 10 30
2 5 30 20
2 6 40 60
8
0 1 2 3 4 5 6 7

Sample Output 1:

Okay.
You may take test 0 directly.
0->1
0->1->2
You may take test 3 directly.
0->1->2->4
0->1->2->4->5
0->1->2->6
3->7

Sample Input 2:

4 5
0 1 1 10
1 2 2 10
3 0 4 10
3 2 5 10
2 0 3 10
2
3 1

Sample Output 2:

Impossible.
You may take test 3 directly.
Error.

題目限制:

image.png

題目大意:

有N個考試,M個考試的相互之間的關係,如今咱們把這N場考試和M條關係稱之爲一個考試計劃,請問當前的計劃是不是一個consistent的計劃,並給出了K場考試的查詢,若是是一個consistent的計劃,要求給出經過當前考試最爲快速的方法,若是有多種,選擇得到代金券最多的那種。若是不是一個consistent的計劃,那麼判斷當前考試T是否能夠直接考取,若是是就輸出You may take test T directly.不然輸出Error.code

算法思路:

首先得理解題意,plan是指每個測試用例,而不是每個查詢,consistent的判斷方法就是判斷當前圖中是否有環,因爲是有向圖,可使用拓撲排序來進行判斷.
對於含有環的圖,須要知道當前查詢的結點是否入度爲0,所以使用zeroDegree集合保存每個入度爲0的結點(inDegree在拓撲排序的時候會發生變化),對於入度爲0的點直接輸出You may take test T directly.就好,不然輸出Error.
對於不含有環的圖,咱們須要找到一條到查詢結點的最短路徑,這裏使用迪傑斯特拉算法解決,可是沒有給定起點,仔細讀題發現起點必定是那些讀入爲0的結點,由於只有入度爲0的點才能沒有任何前置條件進行考試,可是若是對每個入度爲0的起點進行遍歷得到最短路徑的話,最後兩個測試點會運行超時,因此這裏採用一個小技巧,咱們認爲設立一個頂點,其入度爲0,而且將該頂點指向當前圖中的全部入度爲0的頂點,而且邊權設置爲0,這樣整個圖中入度爲0的頂點就剛好只有一個了,每個頂點的最短路徑的獲取只須要執行一遍迪傑斯特拉算法便可。

這裏簡要說明下爲何該方法可行,由於從該點出發,必定先經原圖中入度爲0的起點,也就是真正的起點,那麼該圖中全部結點的最短路徑必定是比較了全部原圖中起點以後所得到的,而且邊權爲0,不會影響最短距離。

而後對於每個查詢的考生科目,判斷是否在原圖中是入度爲0的結點,若是是,輸出You may take test T directly.,不然使用DFS遍歷該結點的路徑(在獲取最短距離的時候,每個結點的前驅保存在了pre數組中,所以能夠直接獲取)

注意點:

  • 一、若是屢次使用迪傑斯特拉算法,最後兩個測試點會超時。

提交結果:

image.png

AC代碼:

#include<cstdio>
#include<queue>
#include<string>
#include<cstring>
#include<unordered_set>

using namespace std;

int N,M;//頂點數和邊數
vector<int> Adj[1005];//鄰接表
int score[1005][1005];//score[a][b]表明a測試點得到的最低得分能夠有資格考b考試
int voucher[1005][1005];//voucher[a][b]表明a測試點得到score[a][b]及以上分數能夠獲得考b的代金券
int inDegree[1005];// 每個頂點的入度
bool inQueue[1005];// 標記已經入隊的節點
unordered_set<int> zeroDegree;// 圖中全部入度爲0的頂點,這裏指原圖中入度爲0的頂點 

// 拓撲排序判斷是否有環存在 
bool topologicalOrder(){
    queue<int> q;
    int num = 0;
    // 入隊全部入度爲0的頂點
    for(int i=0;i<N;++i){
        if(inDegree[i]==0){
            inQueue[i] = true;
            q.push(i);
        }
    }
    while (!q.empty()){
        int t = q.front();
        q.pop();
        ++num;//統計入隊的結點個數 
        // 將t的臨界點的入度所有減一
        for(int vertex:Adj[t]){
            --inDegree[vertex];
            // 將入度爲0且沒有入隊的節點入隊
            if(inDegree[vertex]==0){
                q.push(vertex);
            }
        }
    }
    return num==N;// true表示沒有環 
}

int dis[1005];// 每個節點到起點的最短距離(分數)
int money[1005];// 每個節點到起點得到到最大代金券
bool visited[1005];// 標記每個節點是否訪問
int pre[1005];// 每個節點的前驅節點

void Dijsktra(int start){
    fill(dis,dis+1005,0x3ffffff);
    dis[start] = 0;
    money[start] = 0;
    for(int i=0;i<N+1;++i){// N+1個頂點 
        // 找出距離起點最短的未訪問節點
        int min_dis = 0x3fffff;
        int min_index = -1;
        for(int j=0;j<N+1;++j){
            if(!visited[j]&&dis[j]<min_dis){
                min_dis = dis[j];
                min_index = j;
            }
        }
        if(min_index==-1) return;
        visited[min_index] = true;
        // 優化路徑
        for(int j=0;j<Adj[min_index].size();++j){
            int v = Adj[min_index][j];
            if(!visited[v]){
                if(dis[v]>dis[min_index]+score[min_index][v]){
                    // 當前路徑更短
                    pre[v] = min_index;
                    dis[v] = dis[min_index]+score[min_index][v];
                    money[v] = money[min_index] + voucher[min_index][v];
                } else if(dis[v]==dis[min_index]+score[min_index][v]&&money[v]<money[min_index]+voucher[min_index][v]){
                    // 須要考試的分數同樣,可是得到的代金券更多
                    pre[v] = min_index;
                    money[v] = money[min_index] + voucher[min_index][v];
                }
            }
        }
    }
}

void DFS(int end){
    if(pre[end]==N){
        // 到達起點 
        printf("%d",end);
        return;
    }
    DFS(pre[end]);
    printf("->%d",end);
}

void consistent(int query[],int K){
    Dijsktra(N);// 得到每個結點的最短距離和路徑 
    for(int i=0;i<K;++i){
        if(zeroDegree.find(query[i])!=zeroDegree.end()){
            // 當前考試沒有前置條件
            printf("You may take test %d directly.",query[i]);
        }else{
            DFS(query[i]);
        }
        if(i<K-1) printf("\n");
    }
}

void notConsistent(int query[],int K){
    for(int i=0;i<K;++i){
        if(zeroDegree.find(query[i])!=zeroDegree.end()){
            // 當前考試沒有前置條件
            printf("You may take test %d directly.",query[i]);
        }else{
            printf("Error.");
        }
        if(i<K-1) printf("\n");
    }
}

int main(){
    scanf("%d %d",&N,&M);
    for (int i = 0; i < M; ++i) {
        int a,b;
        scanf("%d %d",&a,&b);
        scanf("%d %d",&score[a][b],&voucher[a][b]);
        ++inDegree[b];
        Adj[a].push_back(b);
    }
    // 添加一個入度爲0的頂點,頂點編號爲N,與全部入度爲0的頂點鏈接一條邊,這樣N就是惟一的入度爲0的頂點了 
    for(int i=0;i<N;++i){
        if(inDegree[i]==0){
            Adj[N].push_back(i);
            zeroDegree.insert(i);
        }
    }
    int K;
    scanf("%d",&K);
    int query[K];
    for(int i=0;i<K;++i){
        scanf("%d",&query[i]);
    }
    bool isOk = topologicalOrder();
    if(isOk){
        printf("Okay.\n");
        consistent(query,K); 
    } else {
        printf("Impossible.\n");
        notConsistent(query,K); 
    }
    return 0;
}
相關文章
相關標籤/搜索