PAT甲級 圖 相關題_C++題解

PAT (Advanced Level) Practice 用到圖的存儲方式,但沒有用到圖的算法的題目ios

目錄

  • 1122 Hamiltonian Cycle (25)
  • 1126 Eulerian Path (25)
  • 1134 Vertex Cover (25)
  • 1142 Maximal Clique (25)
  • 1154 Vertex Coloring (25)

1122 Hamiltonian Cycle (25)

題目思路

  • n != queryV.size() 檢查是否 query 覆蓋了全部結點
  • kn != n + 1 檢查 query 是否多走或少走
  • query[0] != query[kn-1] 檢查是否成環
  • 遍歷 query 檢查是否每一步均可到達
#include<iostream>
#include<unordered_set>
using namespace std;
bool G[201][201] = {false};
int main()
{
    int n, m, u, v, k, kn;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++){
        scanf("%d%d", &u, &v);
        G[u][v] = G[v][u] = true;
    }
    scanf("%d", &k);
    for (int i = 0; i < k; i++){
        scanf("%d", &kn);
        int query[kn];
        unordered_set<int> queryV;
        for (int j = 0; j < kn; j++){
            scanf("%d", &query[j]);
            queryV.insert(query[j]);
        }
        bool isC = true;
        for (int j = 0; j < kn - 1; j++)
            if (!G[query[j]][query[j+1]]) isC = false;
        if (!isC || kn != n + 1 || n != queryV.size() || query[0] != query[kn-1]) printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}

1126 Eulerian Path (25)

題目思路

  • 用鄰接表存儲圖,能夠用每一個結點對應鄰接點的個數(Adj[i].size())表示每一個結點的度
  • 題幹給出根據度奇偶性和個數判斷的先決條件是要是連通圖
  • 先用深搜判斷連通,從任一結點開始,看能夠遍歷到多少結點
  • 若遍歷到的結點數與結點總數不一樣,則不是連通圖,直接輸出 Non-Eulerian
  • 若相同則說明是連通圖,再遍歷鄰接表輸出結點度數並記錄度數爲奇數的結點個數
  • 根據奇度數結點個數輸出是否爲 Eulerian
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int> Adj[501];
bool vis[501] = {false};
int connected = 0;
void DFS(int root){
    connected++;
    vis[root] = true;
    for (int i = 0; i < Adj[root].size(); i++)
        if (!vis[Adj[root][i]]) DFS(Adj[root][i]);
}
int main()
{
    int n, m, u, v, oddnum = 0;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++){
        scanf("%d%d", &u, &v);
        Adj[u].push_back(v);
        Adj[v].push_back(u);
    }
    DFS(1);
    for (int i = 1; i < n + 1; i++){
        printf("%d%c", Adj[i].size(), i == n ? '\n' : ' ');
        if (Adj[i].size() % 2) oddnum++;
    }
    if (connected != n) printf("Non-Eulerian\n");
    else printf("%s\n", !oddnum ? "Eulerian" : oddnum == 2 ? "Semi-Eulerian" : "Non-Eulerian");
    return 0;
}

簡化前代碼

  • 用鄰接矩陣存儲圖
  • 單獨開數組記錄結點度數
  • 用 BFS 判斷是否連通
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int G[501][501] = {0};
bool vis[501] = {false};
int main()
{
    int n, m, u, v, connected = 0, oddnum = 0;
    scanf("%d%d", &n, &m);
    int degree[n+1] = {0};
    for (int i = 0; i < m; i++){
        scanf("%d%d", &u, &v);
        degree[u]++;
        degree[v]++;
        G[u][v] = G[v][u] = 1;
    }
    queue<int> q;
    q.push(n);
    vis[n] = true;
    while (!q.empty()){
        int now = q.front();
        q.pop();
        connected++;
        for (int i = 1; i < n + 1; i++){
            if (!vis[i] && G[now][i]){
                q.push(i);
                vis[i] = true;
            }
        }
    }
    for (int i = 1; i < n + 1; i++){
        printf("%d%c", degree[i], i == n ? '\n' : ' ');
        if (degree[i] % 2) oddnum++;
    }
    if (connected != n) printf("Non-Eulerian\n");
    else printf("%s\n", !oddnum ? "Eulerian" : oddnum == 2 ? "Semi-Eulerian" : "Non-Eulerian");
    return 0;
}

1134 Vertex Cover (25)

題目思路

  • vector<pair<int,int>> 保存邊的端點
  • 對每一個 query,新建一個 map 標記給出的 vertex
  • 遍歷全部邊,檢查是否有邊兩個端點均不在給出的 vertex 中
  • 如有說明給出的 vertex 不能覆蓋全部邊,標記變量退出循環
  • 按標記變量輸出要求內容
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
int main()
{
    int n, m, k, nv, v;
    scanf("%d%d", &n, &m);
    vector<pair<int,int>> edges(m);
    for (int i = 0; i < m; i++) scanf("%d%d", &edges[i].first, &edges[i].second);
    scanf("%d", &k);
    for (int i = 0; i < k; i++){
        scanf("%d", &nv);
        unordered_map<int,bool> vertex;
        bool iscover = true;
        for (int j = 0; j < nv; j++){
            scanf("%d", &v);
            vertex[v] = true;
        }
        for (int j = 0; j < m; j++){
            if (!vertex[edges[j].first] && !vertex[edges[j].second]){
                iscover = false;
                break;
            }
        }
        printf("%s\n", iscover ? "Yes" : "No");
    }
    return 0;
}

1142 Maximal Clique (25)

題目思路

  • 輸入無向邊,用鄰接矩陣將兩個方向的邊均存儲起來
  • 每輸入一個待檢查的序列,就將標記變量 isclique & isMax 均設爲 true,新建保存序列的數組和保存序列結點的集合
  • 輸入待查序列同時將結點壓入集合
  • 首先用二重循環檢查序列是否兩兩相鄰,若不是,說明現有序列非 clique,設置標記輸出內容並跳出循環
  • 若經過上個檢查,已知是 clique,要檢查是不是最大的,也就是是否有其餘結點與序列中每一個點都相鄰
  • 用一個變量按順序遍歷結點標號,取出不在序列中的結點,與序列結點兩兩配對檢查是否相鄰
    • 如有一對不相鄰就從序列結點中 break 取下一個結點
    • 若能一直檢查到序列結尾,發現此結點與序列每一個結點均相鄰,說明現有序列非 max clique,設置標記輸出內容跳出循環
  • 最後檢查變量按要求輸出內容
#include<iostream>
#include<set>
using namespace std;
bool G[201][201] = {false};
int main()
{
    int nv, ne, u, v, m, K;
    scanf("%d%d", &nv, &ne);
    for (int i = 0; i < ne; i++){
        scanf("%d%d", &u, &v);
        G[u][v] = G[v][u] = true;
    }
    scanf("%d", &m);
    for (int i = 0; i < m; i++){
        bool isclique = true, isMax = true;
        scanf("%d", &K);
        int clique[K];
        set<int> cliqueV;
        for (int j = 0; j < K; j++){
            scanf("%d", &clique[j]);
            cliqueV.insert(clique[j]);
        }
        for (int j = 0; j < K - 1; j++){
            for (int k = j + 1; k < K; k++){
                if (!G[clique[j]][clique[k]]){
                    isclique = false;
                    printf("Not a Clique\n");
                    break;
                }
            }
            if (!isclique) break;
        }
        if (isclique){
            for (int j = 1; j <= nv; j++){
                if (cliqueV.find(j) == cliqueV.end()){
                    for (int k = 0; k < K; k++){
                        if (!G[clique[k]][j]) break;
                        if (k == K - 1) isMax = false;
                    }
                }
                if (!isMax){
                    printf("Not Maximal\n");
                    break;
                }
            }
            if (isMax) printf("Yes\n");
        }
    }
    return 0;
}

1154 Vertex Coloring (25)

題目思路

  • vector<pair<int,int>>保存全部邊
  • 將全部點的顏色存起來,同時放入set統計顏色個數
  • 枚舉全部邊,檢查是否每條邊的兩點個顏色是否相同
  • 如有相同的邊,設置標記
  • 根據標記 輸出顏色個數 或 輸出No
#include<iostream>
#include<vector>
#include<set>
using namespace std;
int main()
{
    int n, m, k, u, v;
    scanf("%d%d", &n, &m);
    vector<pair<int,int>> edges(m);
    for (int i = 0; i < m; i++)
        scanf("%d%d", &edges[i].first, &edges[i].second);
    scanf("%d", &k);
    for (int i = 0; i < k; i++){
        vector<int> colors(n);
        set<int> coloring;
        bool isokay = true;
        for (int j = 0; j < n; j++){
            scanf("%d", &colors[j]);
            coloring.insert(colors[j]);
        }
        for (int j = 0; j < m; j++)
            if (colors[edges[j].first] == colors[edges[j].second])
                isokay = false;
        if (!isokay) printf("No\n");
        else printf("%d-coloring\n", coloring.size());
    }
    return 0;
}
相關文章
相關標籤/搜索