最小生成樹Prime算法(hdu1233還是暢通工程 、hdu1863暢通工程)

在無向加權圖中,n個頂點的最小生成樹有n-1條邊,這些邊使得n個頂點之間可達,且總的代價最小。
prim算法是一種貪心算法,將全部的頂點劃分爲2個集合,每次總在2個集合之間中找最小的一條邊,局部最優最終達到全局最優,這正是貪心的思想。
具體的描述參見相關書籍:

描述

從單一頂點開始,普里姆算法按照以下步驟逐步擴大樹中所含頂點的數目,直到遍及連通圖的所有頂點。

1.  輸入:一個加權連通圖,其中頂點集合爲V,邊集合爲E;

2.  初始化:Vnew ={x},其中x爲集合V中的任一節點(起始點),Enew ={};

3.  重複下列操作,直到Vnew =V:

1.  在集合E中選取權值最小的邊(u, v),其中u爲集合Vnew中的元素,而v則不是(如果存在有多條滿足前述條件即具有相同權值的邊,則可任意選取其中之一);

2.  將v加入集合Vnew中,將(u, v)加入集合Enew中;

4.  輸出:使用集合Vnew和Enew來描述所得到的最小生成樹。

 

  

prim的實現:
1 //prim最小生成樹
 2     
 3     public Edge[] getEdges(int position){    //返回從頂點position開始的最小生成樹的邊數組
 4         Edge[] edges = new Edge[size()-1];    //最小生成樹裏邊數爲n-1,!!!!size()
 5         VNodes[position].setVisited(true);    //將原來的遍歷中用的標誌在這分離集合
 6         for(int i = 0;i <edges.length;i++)    //找n-1條邊出來
 7         {
 8             Edge edge =getMinEdge(VNodes);        //在當前分離的2個集合之間找到最小邊
 9             edges[i] = edge;
10             VNodes[edge.getEnd()].setVisited(true);//將新添加的邊的另一端的頂點分離出來
11         }
12         return edges;
13     }
14     
15     private Edge getMinEdge(VNode[] VNodes){   //從分離的2個集合之間求出最小的邊
16         return min;
17     }
18     
19     private  boolean hasEdge(VNode node){   //判斷某個標記true的頂點跟另一個集合之間是否有邊
20         return false;
21     }
22     
23     
24     private Edge getMinEdgeFrom(VNode node){ //如果有邊(前提),求出這個頂點到對方集合的最小邊
25         return min;
26     }
27     
28     

因爲我存的是頂點,所以還要找邊,比較麻煩一點,如果在圖中記錄了一個邊得數組,就可以直接在邊得數組裏面去找最小邊。
解決問題的思路是先很容易就可以寫出上述最小生成樹的邏輯實現,然後去一一實現支持它的方法。

三個支持方法的實現:

private Edge getMinEdge(VNode[] VNodes){   //從分離的2個集合之間求出最小的邊
        Edge min = null;
        //for(int i = 0;i < VNodes.length;i++)
        for(int i = 0;i <size();i++)
        {
            if(VNodes[i].getVisited() && hasEdge(VNodes[i]))//從true集合向false集合找
            {
                Edge temp =getMinEdgeFrom(VNodes[i]);
                if(min == null)//第一次的初始化min
                    min = temp;
                else if(temp.getLen()< min.getLen())
                    min = temp;
            }
        }
        return min;
    }
    
    private  boolean hasEdge(VNode node){   //判斷某個標記true的頂點跟另一個集合之間是否有邊
        if(node.getFirst() == null)
            return false;
        else
        {
            Edge temp = node.getFirst();
            while(temp != null)
            {
                int index = temp.getEnd();
                if(VNodes[index].getVisited() == false)
                    return true;
                else temp = temp.getNext();
            }
        }
        return false;
    }


  private Edge getMinEdgeFrom(VNode node){ //如果有邊(前提),求出這個頂點到對方集合的最小邊
        Edge min = null;
        Edge temp = node.getFirst();
        
        while(temp != null)
        {
            int index = temp.getEnd();
            if(VNodes[index].getVisited() == false)//說明此時的temp是到對方集合的一條邊
            {
                if(min == null)
                    min = temp;
                else if(temp.getLen()< min.getLen())
                    min = temp;
            }
            temp = temp.getNext();
        }
        return min;
    }
   


然後添加一個圖來測試一下:

public static void main(String[] args) {
        // TODO Auto-generated method stub
        UnDirectedGraph g = new UnDirectedGraph();
        
        for(int i = 1;i <7;i++)
            g.addVNode("V" +i);
        g.addEdge(0, 1, 6);
        g.addEdge(0, 2, 1);
        g.addEdge(0, 3, 5);
        g.addEdge(1, 2, 5);
        g.addEdge(1, 4, 5);
        g.addEdge(2, 3, 5);
        g.addEdge(2, 4, 6);
        g.addEdge(2, 5, 4);
        g.addEdge(3, 5, 2);
        g.addEdge(4, 5, 6);//嚴蔚敏數據結構中的那個圖
        
        Edge[] edges = g.getEdges(0);
        System.out.println("輸出最小生成樹的邊");
        for(int i = 0;i <edges.length;i++)
        {
            int start = edges[i].getStart();
            int end = edges[i].getEnd();
            System.out.println("邊: " + g.VNodes[start].getVNode() +"---" +g.VNodes[end].getVNode() 
                    + "   長度:" + edges[i].getLen());
        }

}


結果:

輸出最小生成樹的邊: 

邊: V1---V3   長度:1
邊: V3---V6   長度:4
邊: V6---V4   長度:2
邊: V3---V2   長度:5
邊: V2---V5   長度:5

可以發現這5條邊是跟圖中生成過程的順序一樣,依次找到放入數組的。

更多相關資料http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html

例題 hdu1233還是暢通工程

模板題目

package 最小生成樹;

import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
public class hdu還是暢通工程 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(new InputStreamReader(System.in));
		boolean []v;
		int [][]map;
		int a,b,INF=100000000;
		while(true){
			int N = sc.nextInt();
			if(N==0)break;
			int M = N*(N-1)/2;
			map = new int[N][N];
			for(int i=0;i<N;i++)map[i][i] = INF;
			v = new boolean[N];
			for(int i=0;i<M;i++){
				a = sc.nextInt();
				b = sc.nextInt();
				map[a-1][b-1] = map[b-1][a-1] = sc.nextInt();
			}
			int falg = 0;
			int MIN ;
			int SUM = 0;
			v[0] = true;
			for(int i=1;i<N;i++){//循環N-1次代表構造好了最小生成樹
				MIN = 1000000;
				for(int j=0; j<N; j++)
					if(!v[j]&& MIN > map[0][j]){
						MIN = map[0][j];
						falg = j;	
					}
				  v[falg] = true;
				  SUM += MIN;
				  for(int j=0; j<N; j++){
					  if(!v[j] && map[0][j]>map[falg][j]){//參見數據結構
						  map[0][j] = map[falg][j];
					  }
				  }
				}
			System.out.println(SUM);
			}	
		}}
	}


hdu1863 暢通工程:

其實該題用kruskal算法最簡便。

出於練習的心態於是乎,先用prime做了一下,開始時並查集+prime。最後上網找找該題的prime解法,發現直接用prime算出最小路徑---順便判斷圖的連通性更簡便

import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
public class hdu1863暢通工程_最小生成樹{
   static int map[][],fa[],n,m,a,b,c,INF=100000000;
   static  boolean v[],flag;
	public static void main(String[] args) {
       Scanner sc = new Scanner(new InputStreamReader(System.in));
  
       while(true){
    	   n = sc.nextInt();//道路條數
    	   m = sc.nextInt();//村莊數
    	   if(n==0)break;
    	   map = new int[m+1][m+1];
    	   v = new boolean[m+1];
    	
    	   for(int i=1;i<=m;i++)
    		   for(int j=i;j<=m;j++)
    			 map[j][i]=map[i][j]=INF;
    	   
    	   for(int i=1;i<=n;i++){
    		   a = sc.nextInt();
    		   b = sc.nextInt();
    		   c = sc.nextInt();
    		   if(map[a][b]>c)
    			 map[a][b]=map[b][a]=c;//更新路長
    	   }int sum;
    	   if((sum=prime())==-1)
    		   System.out.println("?");
    	   else
    		   System.out.println(sum);
       }
	}

	private static int prime() {

		v[1] = true;
		 int MIN,k,sum=0;
		 for(int i=1;i<m;i++){
			 MIN = 10000000;
			 k = -1;
			 for(int j=1;j<=m;j++)
				 if(!v[j]&& MIN>map[1][j]){
					 MIN = map[1][j];
					 k = j;
				 }
			 if(k==-1)//判斷是否有最小生成樹
				 return -1;
			 
			 v[k] = true;
			 sum += MIN;
			 for(int j=1;j<=m;j++){
				 if(!v[j]&& map[1][j] > map[k][j])
					 map[1][j] = map[k][j];
			 }
		 }
		return sum;
	}
}

並查集 + prime

import java.io.InputStreamReader;
import java.util.Scanner;
public class hdu1863暢通工程_最小生成樹 {
    static int[]fa;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int map[][];
		int INF = 1000000000; 
		boolean[]v;
       Scanner sc = new Scanner(new InputStreamReader(System.in));
       while(true){
    	   int n = sc.nextInt();//路的條數
    	   int m = sc.nextInt();//村莊數
    	   if(n==0)break;
    	   
    	   map = new int[m][m];
    	   v = new boolean[m];
    	   fa = new int[m+1];
    	   for(int i=1;i<=m;i++)fa[i] = i;
    	   for(int i=0;i<m;i++)
    		   for(int j=i;j<m;j++)
    			   map[i][j]=map[j][i]=INF;
    	 
    	   for(int i=0;i<n;i++){
    		  int a = sc.nextInt();
    		  int b = sc.nextInt();
    		  Union(a,b);
    		  map[a-1][b-1]=map[b-1][a-1]=sc.nextInt();
    	   }
    	   int Count=0;
    	   for(int i=1;i<=m;i++)
    		   if(i==fa[i])
    			   Count++;
    	   
    	   if(Count!=1){
    		   System.out.println("?");
    		   continue;
    	   }
    	   int MIN,sum=0,falg = 0;
    	    v[0] = true;
    	   for(int i=1;i<m;i++){
    		   MIN = 100000000;
    		   for(int j=0;j<m;j++)
    			   if(!v[j] && MIN>map[0][j]){
    				   MIN = map[0][j];
    				   falg = j;
    			   }
    		   v[falg] = true;
    		   sum += MIN;
    		   for(int j=0;j<m;j++){
    			   if(!v[j]&& map[0][j]>map[falg][j])
    				   map[0][j]=map[falg][j];
    		   }
    	   }
    	   System.out.println(sum);
       }
	}

	private static void Union(int a, int b) {
		int x = find(a);
		int y = find(b);
		if(x!=y)
			fa[y] = x;
	}
	private static int find(int b) {
		while(b!=fa[b])
			b = fa[b];
		return b;
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章