PAT_A1013 Battle Over Cities

文章目录

  • 一 题意分析
  • 二 代码实现
    • 1 DFS
    • 2 并查集+路径压缩

题目链接: PAT_A1013

一 题意分析

分析题意:
首先必须要知道,给出的是一个连通图,若使其中一个结点连接的线全部断掉,该连多少条线使这个图继续保持连通(没有问怎么连,而是问连多少条线)。
断掉之后,一个连通图变成了n个连通块,而需要连接的边的条数为连通块条数减一。
所以问题转化为:
求一个图抹去某些边之后它的连通块数量,减去1。
关键词: 图的遍历, 连通块

二 代码实现

那么有两种常用方法:

1 DFS

这里采用邻接表的方式进行存储

#include
#include
#include

using namespace std;

const int N = 1005;
int n, m, k; // number of city, number of highway, number of check

vector<int> G[N];
bool vis[N];
int cur; // 代表当前删除结点的编号

void dfs(int p) {
    if (p == cur) return;
    vis[p] = true;
    for (int i=0; i<G[p].size(); ++i) {
        int id = G[p][i];
        if (!vis[id]) {
            dfs(id);
        }
    }
}

int check() {
    memset(vis, false, sizeof(vis));
    // 计算当前图中连通块的个数
    int block = 0;
    while (true) {
        int id = -1;
        for (int i=0; i<n; ++i) {
            if (i != cur && !vis[i]) {
                id = i;
                break;
            }
        }
        if (id == -1) break;
        dfs(id);
        block++;
    }

    return block-1;
}

int main() {
    int from, to;

    scanf("%d %d %d", &n, &m, &k);
    for (int i=0; i<m; ++i) {
        scanf("%d %d", &from, &to);
        from--;
        to--;
        G[from].push_back(to);
        G[to].push_back(from);
    }

    for (int i=0; i<k; ++i) {
        scanf("%d", &cur);
        cur--;
        printf("%d\n", check());
    }

    return 0;
}

2 并查集+路径压缩

如果不用路径压缩,会超时

#include
#include

using namespace std;

const int N = 1005;

vector<int> G[N];
int father[N]; // 存放各结点的父亲结点编号
bool vis[N];

int findFather(int x) {
    // 查找 x 所在集合的根节点
    int a = x;
    while (x != father[x]) {
        x = father[x];
    }

    // 路径压缩
    while (a != father[a]) {
        int z = a;
        a = father[a];
        father[z] = x;
    }

    return x;
}

void Union(int a, int b) {
    // 合并 a 与 b 所在的集合
    int fa = findFather(a);
    int fb = findFather(b);

    if (fa != fb) {
        father[fa] = fb;
    }
}

void init() {
    for (int i=0; i<N; ++i) {
        father[i] = i;
        vis[i] = false;
    }
}

int n, m, k;

int main() {
    int from, to;

    scanf("%d %d %d", &n, &m, &k);
    for (int i=0; i<m; ++i) {
        scanf("%d %d", &from, &to);
        from--;
        to--;
        G[from].push_back(to);
        G[to].push_back(from);
    }

    int cur; // 当前删除的结点编号
    for (int i=0; i<k; ++i) {
        scanf("%d", &cur);
        init();
        cur--;
        
        for (int i=0; i<n; ++i) {
            for (int j=0; j<G[i].size(); ++j) {
                int u = i, v = G[i][j];
                if (u == cur || v == cur) continue;
                Union(u, v); // 因为这两个点有边相连,因此把它们归类到一个集合中
            }
        }
        int block = 0;
        for (int i=0; i<n; ++i) {
            if (i == cur) continue;
            int fi = findFather(i);
            if (vis[fi] == false) {
                block++;
                vis[fi] = true; // 将当前连通块根节点设置为已访问
            }
        }

        printf("%d\n", block-1);
    }

    return 0;
}

你可能感兴趣的:(图论)