P2941 [USACO09FEB]Surround the Islands S 题解

· · 题解

1.题目大意

这道题我认为配不上蓝题,我觉得这不科学,这道题的神奇之处在于它的题面描述十分的神奇。

中文翻译可能有一些问题,我们看看英文翻译,它要求我们在去到一个岛后立马回到原来的岛。这不就是一个菊花图吗??

2.代码实现

我们先用并查集来把环缩掉,然后直接枚举以每个节点为中心的菊花图,记得把答案乘2(我们要去一趟,回来一趟,共两趟),时间复杂度O(n^2)

有些大佬用Tarjan缩点,这样未免太麻烦,这个图是一个不联通的,每两个环之间没有其他的边连接!!!

#include <bits/stdc++.h>
using namespace std;
int n, fa[505];
int dis[505][505];
int ans = INT_MAX, tot;
int opt[505];
int getfa(int x) { //并查集的核心操作 
    if(x == fa[x]) return x;
    return fa[x] = getfa(fa[x]); //路径压缩优化 
}
int main() {
    cin >> n;
    memset(dis, 127, sizeof(dis)); //初始化两点之间的距离 
    for (int i = 1; i <= n ; i ++) 
        fa[i] = i; //并查集初始化 
    for (int i = 1; i <= n ; i ++) {
        int x, y;
        scanf("%d%d", &x, &y);
        int fx = getfa(x), fy = getfa(y);
        if (fx != fy) //如果在同一个并查集里面,说明这条线段是那个岛屿的一部分 
            fa[fx] = fy;
    }
    for (int i = 1; i <= n ; i ++) //统计总共有多少岛屿,并把它编号 
        if (fa[i] == i) 
            opt[++tot] = i;
    for (int i = 1; i <= n ; i ++) {
        int xxx = getfa(i);
        for (int j = 1; j <= n ; j ++) {
            int yyy = getfa(j);
            int x;
            scanf("%d", &x);
            dis[xxx][yyy] = min(dis[xxx][yyy], x);//在输入的时候计算两个岛屿之间的最短路径
                                                  //注意,我们算的不是最短路,而是两个岛屿之间的直接路径的长度 
        }
    }

    for (int i = 1; i <= tot; i ++) { //以每个岛为菊花图的中心 
        int sum = 0;
        for (int j = 1; j <= tot; j ++) {//枚举其他岛 
            if (i == j)  
                continue;
            sum += dis[opt[i]][opt[j]];
        }
        ans = min(ans, sum);
    }
    cout << (ans << 1) << endl; //输出要乘二!!! 
    return 0;
}

3.注意事项

这个代码中有几个地方需要注意,一个是在答案输出的时候时候要乘二,因为要往返一次,第二个是并查集要记得初始化!!!!(我调这个花了半个小时,我太菜了)。

完结撒花!!