安慰奶牛.java

package A类有价值的回顾的;
/*
 * 最小生成树是什么:连接所有点,而且所有边的权值之和最小
1.概览
Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。
用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,
Kruskal算法在图中存在相同权值的边时也有效。

2.算法简单描述
1).记Graph中有v个顶点,e个边
2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
                if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
                                         添加这条边到图Graphnew中
 */

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

class Edge implements Comparable<Edge>{
    int S;
    int E;
    int L;

    public int compareTo(Edge o) {
        if(this.L > o.L)
            return 1;
        else if(this.L == o.L)
            return 0;
        else 
            return -1;
    }

}


public class 安慰奶牛 {

    public static int[] C = new int[100005];//储存每个牧场里的谈话时间
    public static int min = 100005;
    public static Edge[] e = new Edge[100005];
    public static int N,P;
    public static int[] far = new int[100005];//根节点指向

    private static int find(int x){

        int i, k, r;
        r = x;

        while(far[r] >= 0)//far[r]就是r指向的根节点(farther节点)
            r = far[r];   //跳出的时候,far[r]小于0说明r没有根节点,说明她就是最根的节点

        k = x;
        while(k != r){//让由x节点一直到最根节点中间所有的节点都指向最根节点
             i = far[k];//far[k]已经被储存了
             far[k] = r;
             k = i;
        }

        return r;// 返回最根节点
    }

    static void Union(int S,int E){//将两个节点连接
        int rS,rE;
        int num;
        rS = find(S);//S的最根节点
        rE = find(E);
        num = far[rS] + far[rE];// 最根节点的指向必为负数

        if(far[rS] < far[rE]){//负的越多说明需要被连接的更多,更适合做根节点
            far[rE] = rS;     //
            far[rS] = num;    //
        }
        else{

            far[rS] = rE;
            far[rE] = num;
        }
    }

    private static int Kruskal(){

        int S,E;
        int sumweight = 0,count = 0;
        for(int i=0;i<N;i++)//初始化far,这是新建的图中父子关系的初始化
            far[i] = -1;

        //Arrays.sort(e); 这是不对的,因为这里e的数组中有NULL(100005个并没有完全填充完)
        //Arrays.sort(e, 0, P-1); 这样也不对,最后一个没排
        Arrays.sort(e, 0, P);//对边的权进行由小到大的排序
        for(int i=0;i<P;i++){//贪心算法的核心:从权值最小的路开始依次选择,如果包含了所有的点,就退出
            S = e[i].S;     
            E = e[i].E;

            if(find(S) != find(E)){//如果S的根节点不等于E的根节点,说明没有生成通路,则选择这条边添加到路线中
                sumweight += e[i].L;
                Union(S,E);
                count++;
                if(count >= N-1)//如果有了N-1条通路,说明最小生成树生成了
                    break;
            }
        }
        return sumweight;
    }

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();//N个牧场
        P = sc.nextInt();//P条道路

        for(int i=0;i<N;i++){//输入N个牧场奶牛需要谈话的时间

            C[i] = sc.nextInt();
            if(C[i] < min)//寻找睡觉的地方(最开始当然是需要谈话时间最短的牧场)
                min = C[i];
        }

        for(int i=0;i<P;i++){//输入P条道路行走所需要的时间

            e[i] = new Edge();//千万别忘记要new,否则会NoPoint
            e[i].S = sc.nextInt()-1;//本身C输入的第一个是指S,E = 1,但是下标却是0
            e[i].E = sc.nextInt()-1;//      ↓ 
            e[i].L = sc.nextInt()*2 + C[e[i].S] + C[e[i].E];//这里的时间要考虑所有的时间,包括从一个结点到另一个结点再返回所用时间(包括谈话时间)
        }
        System.out.println(min + Kruskal());
    }
}
/*
 * 问题描述
   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 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。



其实测试用例少了一组,因为路线给有7条,事实只有6条

那么如果改为

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
那么结果是178



知道了这点之后,我们令边值为l,令节点权值为w,那么每个节点的实际权值可以表示为2*l+w,那么我们可以根据这个来求得最小生成树,然后考虑休息点的选择,只需要选最小的节点即可


 */


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值