藍橋杯_演算法訓練_安慰奶牛(用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;
}
}