安慰奶牛 (最小生成树)

问题描述
Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。道路被用来连接N个牧场,牧场被连续地编号为1到N。每一个牧场都是一个奶牛的家。FJ计划除去P条道路中尽可能多的道路,但是还要保持牧场之间 的连通性。你首先要决定那些道路是需要保留的N-1条道路。第j条双向道路连接了牧场Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上 起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的 交谈任务。假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。

输入格式
第1行包含两个整数N和P。

接下来N行,每行包含一个整数Ci。

接下来P行,每行包含三个整数Sj, Ej和Lj。

输出格式
输出一个整数, 所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)。
样例输入
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
样例输出
176
数据规模与约定
5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
这是一道应该是针对C的题目,用java写的代码无论如何都有四组要超时,所有这题重点只要知道实现方法和思路即可

首先假设我们已经生成了一颗树,那么假设我从任意点出发,设为A点,A点是一个特殊的点,因为它需要来回起码两次的访问,这个先放一边,等所有的过程都结束以后我们在考虑。然后我们可以发现 所有的点的访问次数与这个点的度数相等,且举例分析可以发现,所有的边的访问次均为两次,那么我们可以从新定义边的权值,即边本身的w*2加上它连的两个点的权值。最后考虑起点,只要找到权值最小的点,把它加上即可。最小生成树需要并查集,这是不需要说的。
分析结束

import java.util.Arrays;
import java.util.Scanner;


public class Main 
{   
    static int pre[];//并查集的结构
    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int P=sc.nextInt();
        int px[]=new int[n+1];//点的权值
        Edge list[]=new Edge[P];//边的结构,定义见下
        int min=px[1]=sc.nextInt();
        for(int i=2;i<=n;i++)
        {
            px[i]=sc.nextInt();
            if(min>px[i])
                min=px[i];//扎到最小的权值
        }
        int count=0;//计入加入的边数
        for(int i=0;iint x=sc.nextInt();
            int y=sc.nextInt();
            int w=sc.nextInt();
            list[i]=new Edge(x,y,px[x]+px[y]+w*2);//对边的权值的重新定义
        }
        Arrays.sort(list);//从小到大考虑,实现最小生成树(贪心)
        pre=new int[n+1];
        int ans=0;
        for(int i=1;i<=n;i++)
            pre[i]=i;//代表元的初始化
        int x,y,tx,ty;
        for(int i=0;iif(tx!=ty)//如果代表元不相同则合并
            {
                count++;
                ans+=list[i].w;
                pre[tx]=ty;
                if(count==n-1)//如果count是n-1,说明已经找到,提前结束
                    break;
            }
        }
        System.out.println(ans+min);//答案
    }
    static int find(int x)//寻找代表元
    {
        return pre[x]==x?x:(pre[x]=find(pre[x]));//这里用了路径压缩,加速下一次的查找,用递归实现,非常简洁
    }
}
class Edge implements Comparable<Edge>//这是能对边排序所需要的必要接口
{
    int x;
    int y;
    int w;
    Edge(int x ,int y,int w)
    {
        this.x=x;
        this.y=y;
        this.w=w;
    }
    @Override
    public int compareTo(Edge k) {
        if(w>k.w)return 1;
        if(wreturn -1;
        return 0;
    }
}

你可能感兴趣的:(解题报告)