SOJ 4482 忽悠大神【最小生成树】

题目链接:

http://acm.scu.edu.cn/soj/problem.action?id=4482

题意:

给定边权和点权,从一个点出发并回到该点,减少尽量多的边,每路过点和边都要把权重加到花费上,问最小花费是多少?

分析:

容易想到最后得到的肯定是棵树。
每条边肯定走两遍,然后我们在走边的时候它的两个点肯定也要走一遍,把每条边的权值更新为2倍的边权+点权。
出发点要走两遍,所以选择点权最少点作为出发点, 最后最小生成树一发即可。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct edge{int u, v, w;};
const int maxn = 10050, maxm = 100005;
edge e[maxm];
int n, m ,p;
int pa[maxn], _rank[maxn];
int a[maxn];
int _find(int a)
{
    if(a == pa[a]) return a;
    else return pa[a] = _find(pa[a]);
}
void unite(int a, int b)
{
    int ra = _find(a), rb = _find(b);
    if(_rank[ra] < _rank[b]) pa[ra] = rb;
    else{
        pa[rb] = ra;
        if(_rank[ra] == _rank[rb]) _rank[ra]++;
    }
    return;
}
int same(int a, int b)
{
    return _find(a) == _find(b);
}
int kruskal(int r)
{
    sort(e, e+r, cmp);
    for(int i = 1; i <= n; i++){
       pa[i] = i;
    }
    int res = 0;
    for(int i = 0; i < r; i++){
        if(!same(e[i].u,e[i].v)){
            unite(e[i].u, e[i].v);
            res += e[i].w;
        }
    }
    return res;
}
int main(void)
{
    while(~scanf("%d%d", &n, &p)){
        fill(_rank, _rank + n, 1);
        int Min = 1<<31 - 1;
        for(int i = 1;i <= n; i++){
            scanf("%d", &a[i]);
            Min = min(a[i], Min);
        }
        int x, y, r;
        for(int i = 0; i < p;i++){
            scanf("%d%d%d",&x, &y, &r);
            e[i] = (edge){x, y, r*2+a[x]+a[y]};
      }
      printf("%d
", Min + kruskal(p));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Tuesdayzz/p/5758668.html