PAT甲级刷题记录-(AcWing)-Day07图论(6题)和树(2题)

PAT甲级刷题记录-(AcWing)-Day07图论(6题)和树(2题)

课程来源AcWing
其中AcWing中的题目为翻译好的中文题目

1053 Path of Equal Weight

AcWing链接
PAT链接
英语单词

  • assign to 指定给,分配到

解析

  • 使用邻接矩阵来存储边,更加直观
  • 因为路径的长度是计算到叶子节点的,所以在遇到叶子节点的时候判断一下即可。

注意点

  • s + w[i]要在递归中修改,一开始在外面写成s+=w[i]会导致后面同一个节点的孩子传入的时候s累加的越来越大。
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>

const int N = 110;
using namespace std;
int n, m, S, w[N];
bool edge[N][N];
vector<vector<int>> res;

void dfs(int u, int s, vector<int> &path) {
    
    
    cout << "s = " << s << endl;
    bool is_leaf = true;
    // 1. 判断是否是叶子
    for (int i = 0; i < n; ++i) {
    
    
        if (edge[u][i]) {
    
    
            is_leaf = false;
            break;
        }
    }
    if (is_leaf) {
    
    
        // 是叶子的话判断权值是否为S
        if (s == S) res.push_back(path);
    } else {
    
    
        // 不是叶子就遍历下面的节点
        for (int i = 0; i < n; ++i) {
    
    
            if (edge[u][i]) {
    
    
                path.push_back(w[i]);
                dfs(i, s + w[i], path);
                path.pop_back();
            }
        }
    }
}

int main() {
    
    
    cin >> n >> m >> S;
    for (int i = 0; i < n; ++i) {
    
    
        cin >> w[i];
    }
    while (m--) {
    
    
        int id, k;
        cin >> id >> k;
        while (k--) {
    
    
            int node;
            cin >> node;
            edge[id][node] = true;
        }
    }
    vector<int> path = {
    
    w[0]};
    dfs(0, w[0], path);
    sort(res.begin(), res.end(), greater<vector<int>>());
    for (auto &a:res) {
    
    
        cout << a[0];
        for (int i = 1; i < a.size(); ++i) {
    
    
            cout << " " << a[i];
        }
        cout << endl;
    }
    return 0;
}

1094 The Largest Generation

AcWing链接
PAT链接

英语单词

解析

  1. 求每层的节点数量先想到的应该是宽搜, 在搜索的过程中存下当前的节点即可
  2. 也可以递归求深度,然后用数组记录当前深度的值, 这里要注意到节点是从1开始的,因此在遍历是否存在孩子的时候要让i = 1; i <= n

注意点

自己写了个用dfs递归求深度的,可以通过PAT

扫描二维码关注公众号,回复: 15507006 查看本文章
#include <iostream>
#include <algorithm>
#include <cstring>

const int N = 110;
using namespace std;
int n, m;
bool edge[N][N];
int cnt[N], max_depth;

void dfs(int u, int depth) {
    
    
    max_depth = max(depth, max_depth);
    cnt[depth]++;
    for (int i = 1; i <= n; ++i) {
    
    
        if (edge[u][i]) {
    
    
            dfs(i, depth + 1);
        }
    }
}

int main() {
    
    
    cin >> n >> m;
    while (m--) {
    
    
        int id, k;
        cin >> id >> k;
        while (k--) {
    
    
            int node;
            cin >> node;
            edge[id][node] = true;
        }
    }
    dfs(1, 1);
    int res = 0, index = 0;
    for (int i = 1; i <= max_depth; ++i) {
    
    
        if (cnt[i] > res) {
    
    
            res = cnt[i];
            index = i;
        }
    }
    cout << res << " " << index << endl;
    return 0;
}

使用宽度遍历的做法

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

const int N = 110;
using namespace std;
int n, m;
bool edge[N][N];
vector<int> level[N];

int main() {
    
    
    cin >> n >> m;
    while (m--) {
    
    
        int id, k;
        cin >> id >> k;
        while (k--) {
    
    
            int node;
            cin >> node;
            edge[id][node] = true;
        }
    }
    int depth = 1;
    level[1].push_back(1);
    while (!level[depth].empty()) {
    
    
        for (auto &node:level[depth]) {
    
    
            for (int i = 1; i <= n; ++i) {
    
    
                if (edge[node][i]) {
    
    
                    level[depth + 1].push_back(i);
                }
            }
        }
        depth++;
    }
    int res = 0, idx;
    for (int i = 1; i <= n; ++i) {
    
    
        if (level[i].size() > res) {
    
    
            idx = i;
            res = level[i].size();
        }
    }
    cout << res << " " << idx << endl;
    return 0;
}

1003 Emergency

AcWing链接
PAT链接

英语单词

  • scattered 分散的,混乱的

注意点
使用朴素dijkstra算法, 在遍历的过程中记录下每个节点的最短路径条数 和 最大救援人数, 也就是新开两个数组cnt[N], sum[N]来记录。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 520;
int edge[N][N], dist[N];
int people[N]; // 每个城市的救援人数
int n, m, c1, c2; //c1为源点,c2为目标点
int cnt[N]; // 到每个节点的最短路径个数
int sum[N]; // 每个节点的最大救援人数
bool visit[N]; // 节点是否访问

void dijkstra() {
    
    
    dist[c1] = 0;
    cnt[c1] = 1;
    sum[c1] = people[c1];
    for (int i = 0; i < n; ++i) {
    
    
        int t = -1;
        for (int j = 0; j < n; ++j)
            if (!visit[j] && (t == -1 || dist[t] > dist[j])) t = j;
        visit[t] = true;
        for (int j = 0; j < n; ++j) {
    
    
            if (dist[j] > dist[t] + edge[t][j]) {
    
    
                dist[j] = dist[t] + edge[t][j];
                cnt[j] = cnt[t];
                sum[j] = sum[t] + people[j];
            } else if (dist[j] == dist[t] + edge[t][j]) {
    
    
                cnt[j] += cnt[t];
                sum[j] = max(sum[j], sum[t] + people[j]);
            }
        }
    }
}

int main() {
    
    
    cin >> n >> m >> c1 >> c2;
    // 初始化边和权重值
    memset(dist, 0x3f, sizeof(dist));
    memset(edge, 0x3f, sizeof(edge));
    for (int i = 0; i < n; ++i) {
    
    
        cin >> people[i];
    }
    while (m--) {
    
    
        int a, b, c;
        cin >> a >> b >> c;
        edge[a][b] = edge[b][a] = c;
    }
    dijkstra();
    cout << cnt[c2] << " " << sum[c2] << endl;
    return 0;
}

1030 Travel Plan

AcWing链接
PAT链接

英语单词

  • destination 目的地

注意点
与上一题类似, 在需要输出路径的题目里可以使用一个pre[N]来记录前面的节点

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;
const int N = 520;
int edge[N][N], cost[N][N], dist[N];
bool visit[N];
int pre[N];
int sum[N];
int n, m, s, d;

void dijkstra() {
    
    
    memset(dist, 0x3f, sizeof dist);
    memset(sum, 0x3f, sizeof sum);
    dist[s] = 0;
    sum[s] = 0;
    for (int i = 0; i < n; ++i) {
    
    
        int t = -1;
        for (int j = 0; j < n; ++j) {
    
    
            if (!visit[j] && (t == -1 || dist[t] > dist[j])) t = j;
        }
        visit[t] = true;
        for (int j = 0; j < n; ++j) {
    
    
            if (dist[j] > dist[t] + edge[t][j]) {
    
    
                dist[j] = dist[t] + edge[t][j];
                sum[j] = sum[t] + cost[t][j];
                pre[j] = t;
            } else if (dist[j] == dist[t] + edge[t][j]) {
    
    
                // 然后j的花费比较大,就更新
                if (sum[j] > sum[t] + cost[t][j]) {
    
    
                    sum[j] = sum[t] + cost[t][j];
                    pre[j] = t;
                }
            }
        }
    }

}

int main() {
    
    
    memset(edge, 0x3f, sizeof(edge));
    memset(cost, 0x3f, sizeof(cost));
    cin >> n >> m >> s >> d;
    while (m--) {
    
    
        int c1, c2, distance, x;
        cin >> c1 >> c2 >> distance >> x;
        edge[c1][c2] = edge[c2][c1] = distance;
        cost[c1][c2] = cost[c2][c1] = x;
    }
    dijkstra();
    vector<int> res;
    for (int i = d; i != s; i = pre[i]) {
    
    
        res.push_back(i);
    }
    cout << s;
    for (int i = res.size() - 1; i >= 0; i--) {
    
    
        cout << " " << res[i];
    }
    cout << " " << dist[d] << " " << sum[d];
    return 0;
}

!!! 1034 Head of a Gang

AcWing链接
PAT链接

英语单词

解析

  • 用了很多std的操作,因为在图论这边,所以使用了宽搜来判断连通性,其实也可以用并查集做
  • 这边在宽搜的过程中使用visit记录是否访问过,使用nodes记录同一帮派的人
  • 因为记录边的时候a,bb,a的记录都放进去了,在for (auto edge:map[ver]) { 遍历的时候会每条边走两次,所以最后的sun要除以2
#include <iostream>
#include <cstring>
#include <vector>
#include <unordered_map>
#include <algorithm>

using namespace std;
const int N = 1010;
int n, k;

unordered_map<string, vector<pair<string, int>>> map;
unordered_map<string, int> total;
unordered_map<string, bool> visit;

int dfs(string ver, vector<string> &nodes) {
    
    
    visit[ver] = true;
    nodes.push_back(ver);
    int sum = 0;
    for (auto edge:map[ver]) {
    
    
        sum += edge.second;
        string cur = edge.first;
        if (!visit[cur]) sum += dfs(cur, nodes);
    }
    return sum;
}

int main() {
    
    
    cin >> n >> k;
    for (int i = 0; i < n; ++i) {
    
    
        string name1, name2;
        int time;
        cin >> name1 >> name2 >> time;
        map[name1].push_back({
    
    name2, time});
        map[name2].push_back({
    
    name1, time});
        total[name1] += time;
        total[name2] += time;
    }
    vector<pair<string, int>> res;
    for (auto &item:total) {
    
    
        string ver = item.first;
        vector<string> nodes;
        int sum = dfs(ver, nodes) / 2;
        if (nodes.size() > 2 && sum > k) {
    
    
            string boss = nodes[0];
            for (auto people:nodes) {
    
    
                if (total[people] > total[boss])
                    boss = people;
            }
            res.push_back({
    
    boss, nodes.size()});
        }
    }
    sort(res.begin(), res.end());
    cout << res.size() << endl;
    for (auto item:res) {
    
    
        cout << item.first << " " << item.second << endl;
    }
    return 0;
}

按照自己的思路又写了一遍
一开始sum那边忘记除以2了, 导致测试点2过不去
因为在计算total[n]的时候,A给B打电话会让total[A]增加t,total[B]也增加t,在map中会记录为AB打了t分钟,同时BA打了t分钟, 因此在dfs的时候遍历完A还会遍历到B,当他们的total都加入到sum的时候就会导致结果为2t,而实际上他们只打了t分钟

#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 1010;
int n, k;
unordered_map<string, vector<pair<string, int>>> map;
unordered_map<string, int> total;
unordered_map<string, bool> visit;

int dfs(string node, vector<string> &members) {
    
    
    int sum = total[node];
    // 记录当前的节点
    visit[node] = true;
    members.push_back(node);
    // 遍历跟node有打过电话的人
    for (auto &item:map[node]) {
    
    
        string name = item.first;
        if (!visit[name]) sum += dfs(name, members);
    }
    return sum;
}

int main() {
    
    
    cin >> n >> k;
    for (int i = 0; i < n; ++i) {
    
    
        string name1, name2;
        int time;
        cin >> name1 >> name2 >> time;
        map[name1].push_back({
    
    name2, time});
        map[name2].push_back({
    
    name1, time});
        total[name1] += time;
        total[name2] += time;
    }
    vector<pair<string, int>> res;
    for (auto &item:total) {
    
    
        // 遍历每个节点,看看其是否在一个连通图里面
        string name = item.first;
        if (!visit[name]) {
    
    
            // 当前的节点还没被访问过,可能是一个新的gang,进行遍历,计算总数
            vector<string> members;
            int sum = dfs(name, members) / 2;
            if (sum > k && members.size() > 2) {
    
    
                // 满足团队的定义, 找团队里的boss, 也就是通话时间最长的
                string boss = members[0];
                for (auto member:members) {
    
    
                    if (total[member] > total[boss]) {
    
    
                        boss = member;
                    }
                }
                // boss 里面记录的是当前团队里面通话时间最长的老大
                res.push_back({
    
    boss, members.size()});
            }
        }
    }
    cout << res.size() << endl;
    sort(res.begin(), res.end());
    for (auto &c:res) {
    
    
        cout << c.first << " " << c.second << endl;
    }
    return 0;
}

1087 All Roads Lead to Rome

AcWing链接
PAT链接

注意点
很有意思的一道题,把前面的很多知识点融合了起来
目前图论题考的核心都是dijkstra算法,只不过在这个过程中增加了比较的对象,需要记录的东西,以及字符串的映射关系等等.

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <unordered_map>

using namespace std;
const int N = 210;
int n, k;
string city[N]; // 每个城市的名字
unordered_map<string, int> map; // 每个城市的名字与下标的对应关系
int happiness[N]; // 每个城市的幸福感指数
int edge[N][N];
int dist[N], cnt[N], max_happiness[N], sum[N], pre[N]; // 最短路径, 相同的最短路径条数,幸福感和,经过的点的个数(反应平均幸福感)
bool visit[N];

void dijkstra() {
    
    
    dist[1] = 0, cnt[1] = 1, max_happiness[1] = 0;
    for (int i = 0; i < n; ++i) {
    
    
        int t = -1;
        for (int j = 1; j <= n; ++j) {
    
    
            if (!visit[j] && (t == -1 || dist[t] > dist[j])) t = j;
        }
        visit[t] = true;
        for (int j = 1; j <= n; ++j) {
    
    // 用t来更新权重
            if (dist[j] > dist[t] + edge[t][j]) {
    
    
                dist[j] = dist[t] + edge[t][j];
                cnt[j] = cnt[t];
                sum[j] = sum[t] + 1;
                max_happiness[j] = max_happiness[t] + happiness[j];
                pre[j] = t;
            } else if (dist[j] == dist[t] + edge[t][j]) {
    
    
                cnt[j] += cnt[t];
                // If such a route is not unique, the one with the maximum happiness will be recommanded
                if (max_happiness[j] < max_happiness[t] + happiness[j]) {
    
    
                    max_happiness[j] = max_happiness[t] + happiness[j];
                    pre[j] = t;
                    sum[j] = sum[t] + 1;
                } else if (max_happiness[j] == max_happiness[t] + happiness[j]) {
    
    
                    // 选择平均幸福指数最大的,也就是经过的节点sum[]最少的
                    if (sum[j] > sum[t] + 1) {
    
    
                        sum[j] = sum[t] + 1;
                        pre[j] = t;
                    }
                }
            }

        }
    }
}

int main() {
    
    
    memset(edge, 0x3f, sizeof(edge));
    memset(dist, 0x3f, sizeof(dist));
    cin >> n >> k;
    string start;
    cin >> start;
    city[1] = start;
    map[start] = 1;
    for (int i = 2; i <= n; ++i) {
    
    
        cin >> city[i] >> happiness[i];
        map[city[i]] = i;
    }
    for (int i = 0; i < k; ++i) {
    
    
        string name1, name2;
        int x;
        cin >> name1 >> name2 >> x;
        int idx1, idx2;
        idx1 = map[name1];
        idx2 = map[name2];
        edge[idx1][idx2] = edge[idx2][idx1] = min(edge[idx1][idx2], x);
    }
    dijkstra();
    // the number of different routes with the least cost, the cost, the happiness, and the average happiness (take the integer part only) of the recommanded route
    int index = map["ROM"];
    cout << cnt[index] << " " << dist[index] << " " << max_happiness[index] << " " << max_happiness[index] / sum[index]
         << endl;
    vector<string> res;
    for (int i = index; i != map[start]; i = pre[i]) {
    
    
        res.push_back(city[i]);
    }
    cout << start;
    for (int i = res.size() - 1; i >= 0; i--) {
    
    
        cout << "->" << res[i];
    }
    cout << endl;
    return 0;
}

1111 Online Map

AcWing链接
PAT链接

英语单词

  • streets intersections 街道交叉路口/十字路口

解析
使用两次迪杰斯特拉即可。

注意点
写题目的时候遇到几个小坑

  1. 因为本题要使用两次dijkstra算法,这里我只想到了每次用的时候要重置两个dist,但是忘记重置visit矩阵了,就会导致在第二次算法中得不到更新
  2. street += " -> " + to_string(path[i]); 失误写成了 i 小错误
#include <iostream>
#include <vector>
#include <cstring>

using namespace std;
const int N = 520;
int n, m, S, D;
int edge[N][N];
int time_cost[N][N];
int dist1[N], dist2[N], pre[N];
bool visit[N];

pair<int, string> dijkstra(int type) {
    
    
    // type = 1 先考虑距离短(dist1),再考虑时间短(dist2)
    // type = 0 先考虑时间短(dist1),再考虑节点最少(dist2)
    memset(dist1, 0x3f, sizeof(dist1));
    memset(dist2, 0x3f, sizeof(dist2));
    memset(visit, 0, sizeof(visit));
    dist1[S] = 0, dist2[S] = 0;
    for (int i = 0; i < n; ++i) {
    
    
        int t = -1;
        for (int j = 0; j < n; ++j) {
    
    
            if (!visit[j] && (t == -1 || dist1[t] > dist1[j]))
                t = j;
        }
        visit[t] = true;
        for (int j = 0; j < n; ++j) {
    
    
            int w1, w2;
            if (type == 1) w1 = edge[t][j], w2 = time_cost[t][j];
            else w1 = time_cost[t][j], w2 = 1;
            if (dist1[j] > dist1[t] + w1) {
    
    
                dist1[j] = dist1[t] + w1;
                dist2[j] = dist2[t] + w2;
                pre[j] = t;
            } else if (dist1[j] == dist1[t] + w1) {
    
    
                if (dist2[j] > dist2[t] + w2) {
    
    
                    dist2[j] = dist2[t] + w2;
                    pre[j] = t;
                }
            }
        }
    }
    int res = dist1[D];
    vector<int> path;
    for (int i = D; i != S; i = pre[i]) {
    
    
        path.push_back(i);
    }
    string street = to_string(S);
    for (int i = path.size() - 1; i >= 0; i--) {
    
    
        street += " -> " + to_string(path[i]);
    }
    pair<int, string> a = {
    
    res, street};
    return a;
}

int main() {
    
    
    cin >> n >> m;
    memset(edge, 0x3f, sizeof(edge));
    memset(time_cost, 0x3f, sizeof(time_cost));
    for (int i = 0; i < m; ++i) {
    
    
        int v1, v2, flag, length, t;
        cin >> v1 >> v2 >> flag >> length >> t;
        edge[v1][v2] = min(edge[v1][v2], length);
        time_cost[v1][v2] = min(t, time_cost[v1][v2]);
        if (!flag) {
    
    
            edge[v2][v1] = min(length, edge[v2][v1]);
            time_cost[v2][v1] = min(t, time_cost[v2][v1]);
        }
    }
    cin >> S >> D;
    pair<int, string> res_dis = dijkstra(1);
    pair<int, string> res_time = dijkstra(0);
    if (res_dis.second == res_time.second) {
    
    
        printf("Distance = %d; Time = %d: %s", res_dis.first, res_time.first, res_dis.second.c_str());
    } else {
    
    
        printf("Distance = %d: %s\n", res_dis.first, res_dis.second.c_str());
        printf("Time = %d: %s", res_time.first, res_time.second.c_str());
    }
    return 0;
}

1122 Hamiltonian Cycle

AcWing链接
PAT链接

判断回路从一下角度出发即可

  1. 第一个点和最后一个点相同
  2. 每个点直接都有边
  3. 每个顶点都被访问到
  4. 节点的数量刚好为n+1 (防止有重复节点)
    注意点
  • PAT上有一个点卡在了int nodes[N*2]; 要开两倍节点的大小才行
#include <iostream>
#include <cstring>

using namespace std;
const int N = 220;
int n, m, k;
bool edge[N][N], visit[N];
int nodes[N*2];

bool check(int cnt) {
    
    
    memset(visit, 0, sizeof(visit));
    if (nodes[1] != nodes[cnt - 1]) return false; // 回路第一个节点和最后一个节点应该相同
    for (int i = 1; i < cnt; ++i) {
    
    
        visit[nodes[i]] = true; // 遍历的过程中记录下使用到的节点
        if (!edge[nodes[i]][nodes[i + 1]]) return false; //回路中每个节点之间应该有边
    }
    // 判断是否用到了所有的点
    for (int i = 1; i <= cnt; ++i) {
    
    
        if (!visit[i]) return false;
    }
    return cnt != n + 1; // 回路中的点应该刚好为n+1个
}

int main() {
    
    
    cin >> n >> m;
    while (m--) {
    
    
        int a, b;
        cin >> a >> b;
        edge[a][b] = edge[b][a] = true;
    }
    cin >> k;
    while (k--) {
    
    
        int t;
        cin >> t;
        for (int i = 1; i <= t; ++i) {
    
    
            cin >> nodes[i];
        }
        if (check(t)) puts("YES");
        else puts("NO");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Weary_PJ/article/details/124935543