蓝桥杯_算法训练_安慰奶牛(用Kruskal、Prim算法分别实现)

问题描述
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 6
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。

Kruskal算法简述:
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:
先构造一个只含 n 个顶点,而边集为空的子图,
若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。
之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,
也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,
而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;

/** * @author 翔 * */
public class Main {
    private static int nodeNum;//节点个数
    private static int edgeNum;//边数
    private static int[] C;//节点权值
    private static int Cmin;//最小节点权值
    private static ArrayList<Edge> edges=new ArrayList<Edge>();//边集合
    private static ArrayList<Edge> Enew=new ArrayList<Edge>();//Kruskal算法中的Enew
    private static int[] root;//root[i]:代表i节点所在树对应的根节点
    /** * @param args */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        init();
        kruskal();
        System.out.println(caculate());
    }

    //计算所需要的总时间
    private static int caculate(){
        int sum=0;
        for(int i=0;i<Enew.size();i++){
            sum+=Enew.get(i).weight;
        }
        return sum+Cmin;
    }

    //Kruskal算法的主要步骤
    private static void kruskal(){
        for(int i=0;i<edges.size()&&Enew.size()<nodeNum-1;i++){
            Edge edge=edges.get(i);
            if(!inSameTree(edge.from,edge.to)){
                Enew.add(edge);
                root[edge.to]=root[edge.from];
            }
        }
    }

    //判断两个顶点是否在同一棵树里
    private static boolean inSameTree(int from,int to){
        if(findRoot(from)==findRoot(to)){
            return true;
        }else{
            return false;
        }
    }

    //寻找某节点的根节点
    private static int findRoot(int i){
        if(root[i]==i){
            return i;
        }else{
            return root[i]=findRoot(root[i]);
        }
    }

    //初始化各参数的值
    private static void init(){
        Scanner sc=new Scanner(System.in);
        nodeNum=sc.nextInt();
        edgeNum=sc.nextInt();
        C=new int[nodeNum+1];
        root=new int[nodeNum+1];
        for(int i=1;i<=nodeNum;i++){
            C[i]=sc.nextInt();
            root[i]=i;
        }
        findCmin();//寻找出最小节点权值
        for(int i=0;i<edgeNum;i++){
            Edge edge=new Edge(sc.nextInt(),sc.nextInt(),sc.nextInt());
            edges.add(edge);
        }

        //重新计算边权!!!!重点!!!!!!!!!!!!
        for(int i=0;i<edgeNum;i++){
            edges.get(i).weight=edges.get(i).weight*2+C[edges.get(i).from]+C[edges.get(i).to];
        }

        Collections.sort(edges,new EdgeComparator());//按照权值对边集合排序
        sc.close();
    }

    //寻找出最小节点权值
    private static void findCmin(){
        int min=C[1];
        for(int i=2;i<=nodeNum;i++){
            if(C[i]<min){
                min=C[i];
            }
        }
        Cmin=min;
    }
}
class Edge{
    int from;
    int to;
    int weight;
    public Edge(int from,int to,int weight){
        this.from=from;
        this.to=to;
        this.weight=weight;
    }
}

class EdgeComparator implements Comparator<Edge>{

    public int compare(Edge arg0, Edge arg1) {
        // TODO Auto-generated method stub
        return arg0.weight-arg1.weight;
    }

    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> arg0, Comparator<? super U> arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T> Comparator<T> comparingDouble(
            ToDoubleFunction<? super T> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T> Comparator<T> nullsFirst(Comparator<? super T> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T> Comparator<T> nullsLast(Comparator<? super T> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        // TODO Auto-generated method stub
        return null;
    }

    public Comparator<Edge> reversed() {
        // TODO Auto-generated method stub
        return null;
    }

    public Comparator<Edge> thenComparing(Comparator<? super Edge> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public <U extends Comparable<? super U>> Comparator<Edge> thenComparing(
            Function<? super Edge, ? extends U> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public <U> Comparator<Edge> thenComparing(
            Function<? super Edge, ? extends U> arg0, Comparator<? super U> arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    public Comparator<Edge> thenComparingDouble(
            ToDoubleFunction<? super Edge> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public Comparator<Edge> thenComparingInt(ToIntFunction<? super Edge> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public Comparator<Edge> thenComparingLong(ToLongFunction<? super Edge> arg0) {
        // TODO Auto-generated method stub
        return null;
    }

}

Prim算法简述
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew= {x},其中x为集合V中的任一节点(起始点),Enew= {},为空;
3).重复下列操作,直到Vnew= V:
a.在集合E中选取权值最小的边

import java.util.ArrayList;
import java.util.Scanner;

/** * @author 翔 * */
public class Main {

    private static int nodeNum;//节点个数
    private static int edgeNum;//边数
    private static int[] C;//节点权值
    private static int Cmin;//最小节点权值
    private static ArrayList<Edge> edges=new ArrayList<Edge>();//边集合
    private static ArrayList<Edge> Enew=new ArrayList<Edge>();//Prim算法中的Enew
    private static ArrayList<Integer> V_Vnew=new ArrayList<Integer>();//Prim算法中的V-Vnew
    private static ArrayList<Integer> Vnew=new ArrayList<Integer>();//Prim算法中的Vnew

    /** * @param args */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        init();
        prim();
        System.out.println(caculate());
    }

    //计算所需要的总时间
    private static int caculate(){
        int sum=0;
        for(int i=0;i<Enew.size();i++){
            sum+=Enew.get(i).weight;
        }
        return sum+Cmin;

    }

    //Prim算法的主要步骤
    private static void prim(){
        for(int i=2;i<=nodeNum;i++){
            int min=Integer.MAX_VALUE;
            Edge temp=null;
            for (int j = 0; j < edges.size(); j++) {
                if (((V_Vnew.contains(edges.get(j).from)
                        && Vnew.contains(edges.get(j).to))
                        || (Vnew.contains(edges.get(j).from)
                        && V_Vnew.contains(edges.get(j).to)))
                        && edges.get(j).weight < min) {
                    temp = edges.get(j);
                    min = temp.weight;
                }
            }
            if(V_Vnew.contains(temp.to)){
                V_Vnew.remove(new Integer(temp.to));
                Vnew.add(temp.to);
            }else{
                V_Vnew.remove(new Integer(temp.from));
                Vnew.add(temp.from);
            }
            edges.remove(temp);
            Enew.add(temp);         
        }   
    }

    //初始化各参数的值
    private static void init(){
        Scanner sc=new Scanner(System.in);
        nodeNum=sc.nextInt();
        edgeNum=sc.nextInt();
        C=new int[nodeNum+1];
        for(int i=1;i<=nodeNum;i++){
            C[i]=sc.nextInt();
            V_Vnew.add(i);
        }
        int CminFlag=findCmin();//寻找出C[]数组中值最小的数对应的下标
        V_Vnew.remove(new Integer(CminFlag));
        Vnew.add(CminFlag);
        for(int i=0;i<edgeNum;i++){
            Edge edge=new Edge(sc.nextInt(),sc.nextInt(),sc.nextInt());
            edges.add(edge);
        }

        //重新计算边权!!!!重点!!!!!!!!!!!!
        for(int i=0;i<edgeNum;i++){
            edges.get(i).weight=edges.get(i).weight*2+C[edges.get(i).from]+C[edges.get(i).to];
        }
        sc.close();
    }

    //寻找出C[]数组中值最小的数对应的下标
    private static int findCmin(){
        int min=C[1];
        int flag=1;
        for(int i=2;i<=nodeNum;i++){
            if(C[i]<min){
                min=C[i];
                flag=i;
            }
        }
        Cmin=min;
        return flag;
    }
}
class Edge{
    int from;
    int to;
    int weight;
    public Edge(int from,int to,int weight){
        this.from=from;
        this.to=to;
        this.weight=weight;
    }
}

你可能感兴趣的:(算法,Prim,kruskal)