PAT甲级(Advanced Level) Practice 1013 Battle Over Cities

原题

It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.

For example, if we have 3 cities and 2 highways connecting city1​-city2​ and city1​-city3​. Then if city1​ is occupied by the enemy, we must have 1 highway repaired, that is the highway city2​-city3​.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 3 numbers N (<1000), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.

Output Specification:

For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.

Sample Input:

3 2 3
1 2
1 3
1 2 3

Sample Output:

1
0
0

题目翻译

在战争中,所有城市都有高速公路相连,这一点至关重要。如果一个城市被敌人占领,所有进出该城市的公路都会被关闭。我们必须立即知道是否需要修复任何其他公路,以保持其他城市的连接。在标有所有剩余公路的城市地图上,您需要快速判断出需要修复的公路数量。

例如,如果我们有 3 个城市,2 条高速公路连接着城市 1-城市 2 和城市 1-城市 3。如果城市 1 被敌人占领,我们必须修复 1 条公路,即城市 2 至城市 3 的公路。

输入规范

每个输入文件包含一个测试用例。每个案例都以一行开始,其中包含 3 个数字 N(<1000)、M 和 K,分别表示城市总数、剩余高速公路数和需要检查的城市数。然后是 M 行,每行用 2 个整数描述一条公路,这 2 个整数是公路所连接城市的编号。最后一行包含 K 个数字,代表我们关注的城市。

输出规范

针对 K 个城市中的每个城市,在一行中输出如果该城市丢失则需要修复的高速公路数量。

解题思路

第一反应是用链表来储存整张图,但之后想了好久都只有dfs一种方法,好在题目数据量不大,dfs也可以ac(代码1)。换一种思路想想,检验两个城市是否在联通并不需要沿着路一个一个找过来,只用把联通的所有点放在一个集合当中就能快速判断是否联通,于是发现并查集也许是更优解。 

代码1(dfs)

#include <bits/stdc++.h>

using namespace std;

const int N = 1010, M = 1000010;

int n, m, k, a, b;
int h[M], e[M], ne[M], idx;
bool st[N];

void add(int a, int b){                            // 链表储存图
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int x) {                                  // 搜索从x点出发能经过的所有点
    st[x] = true;
    for(int i = h[x]; i != -1; i = ne[i]){
        int j = e[i];
        if(j != a && !st[j]) dfs(j);               // 如果此城市未被摧毁并且没走过,走他
    }
}

int main(){
    cin >> n >> m >> k;
    memset(h, -1, sizeof h);

    for(int i = 0; i < m; i++){
        cin >> a >> b;
        add(a, b), add(b, a);                      // 双向路线,所以来回都添加一次
    }

    while(k--) {
        int ans = -1;
        cin >> a;                                  // 城市a被攻陷,判断不含城市a的连通块数量
        memset(st, false, sizeof st);
        for(int i = 1;i <= n; i++) {
            if(i != a && !st[i]) {                 // 发现了新的连通块
                ans++;
                dfs(i);
            }
        }
        cout << ans << endl;
    }
    
    return 0;
}

代码2(并查集)

#include <bits/stdc++.h>

using namespace std;

const int N = 1010, M = 1000010;

int n, m, k;
int a[M], b[M], p[N];

int find(int x){                                        // 找到x所在的集合
    if(x != p[x]) p[x] = find(p[x]);
    return p[x];
}

int main(){
    cin >> n >> m >> k;
    for(int i = 0; i < m; i++) cin >> a[i] >> b[i];

    for(int i = 0; i < k; i++ ){    
        for(int j = 1; j <= n; j++) p[j] = j;          // 初始所有点都只属于自己的集合
        int x;
        cin >> x;
        int cnt = n - 2;                               // 除去战争城市,还有n - 1个城市,最多n - 2条路
        for(int j = 0; j < m; j++){
            if(a[j] != x && b[j] != x){                // 如果a[j],b[j]不为x,说明a,b之间存在路,需要将b放在a的集合或把a放在b集合
                int pa = find(a[j]), pb = find(b[j]);
                if(pa != pb){                          // 防止存在重复路径
                    p[pb] = pa;
                    cnt--;
                }
            }
        }
        cout << cnt << endl;
    }
    
    return 0;
}

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值