问题描述
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;
}
}