soj 3366 复习最小生成树kruskal

链接:http://cstest.scu.edu.cn/soj/problem.action?id=3366

题目大意:要建井,每个地方都要有,可以选择直接在这里挖井,也可以选择从其他地方已经有井的地方建一条路到这个地方来。

有一段时间没有写过最小生成树了,感觉都有一点快要忘记了的感觉。。。。

这道题之前没有过,借鉴了一下别人的思想。。发现真的好简单。。。而且这种方法在以前使用过,这回居然没有想出来,,要反思一下了。。。感觉跟之前那个只是换了个描述而已····

思路:既然可以挖井,那么也就意味着可以从任意一个井开始挖,甚至还可以每个都用挖的而不建路,这样不方便求最小生成树,但是可以自己设立一个总的起点,这个总起点到各个水井的权值就是挖井所需要的花费,这样我们就转化成了只有一个开始的点,从这个起点出去求最小生成树就可以啦。也能保证至少有一个井是挖出来的

还有一个感觉是一个学长写的题解:http://twocoldzcainiao.sinaapp.com/?p=152

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 309
struct node
{
    int u,v,w;
}edge[M*M];
int p[M];
int k;
int ans;
int cmp(node a,node b)
{
    return a.w < b.w;
}
int find(int x)
{
    return x==p[x]?x:p[x]=find(p[x]);
}
void kruskal()
{
    for(int i = 0;i < k;i++)
    {
        int x = find(edge[i].u); //找出根节点
        int y = find(edge[i].v);
        if(x!=y) //如果根节点不相同(即不在同一个集合之中)就加上这条边
        {
            ans += edge[i].w;
            p[x] = y; //合并节点。
        }
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        k = 0;
        ans = 0;
        for(int i = 0;i <= n;i++)
            p[i] = i;
        for(int i = 1;i <= n;i++)
        {
            int a;
            scanf("%d",&a);
            edge[k].u = 0;
            edge[k].v = i;
            edge[k++].w = a;
        }
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                int a;
                scanf("%d",&a);
                if(i==j) continue;
                edge[k].u = i;
                edge[k].v = j;
                edge[k++].w = a;
            }
        }
        sort(edge,edge+k,cmp);
        kruskal();
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(soj 3366 复习最小生成树kruskal)