最小生成树备忘

最小生成树的题目,不知为何,几个题目算是做的比较顺畅,为防遗忘,再次做出总结
基本上两个思考方式,先贴出自己的模板,再进行解释
基本上两个套路,
prim就拿这个题为例子,hdu1233

package coding;

import java.io.BufferedInputStream;
import java.util.PriorityQueue;
import java.util.Scanner;

public class hdu1233_还是畅通工程 {
	static int []use ,near;         //near数组就是, near[1]代表到1最短是几 ,near[2]代表到2最短是几
	static int[][]pos ,map;
	static int n,m;
	static private final int inf= 0x3f3f3f;

	static int prim() {
		for(int i=1;i<=n;i++) { // prim思想第一步,认为最短边的起点都是1
			near[i]=map[1][i];
			use[i]=0;
		}
		int res=0;
		use[1]=1;   //1不可再使用
		int min, t;
		for(int i=0;i<n-1;i++) {  //这里意思是,你有n个点,我们必将只用找n-1个边就ok了
			min = inf; t =0;
			for(int j=1;j<=n;j++) { //这个循环仅仅为了找 当前的near数组里面谁最小
				if(use[j]==0 && min>near[j]) {
					min = near[j];
					t= j;
				}
			}
			use[t]=1;
			res+=min;  //最短路长度更新变大
			
			//下面这个循环很经典,我们解释一下
			// 我们知道,上面第一次的时候,我们设置 near[n] 就是 1到 n 的距离 
			//但是我们一旦 进行过一次  i 的循环,我们就从near[]数组找个最小的距离,终点为t (上面)
			//此使 1 和 t 都设置成 使用过了 ,假如t是2
			// 那万一  2  到 3 的距离, 比 1 到 3 的距离小,你这个near数组,必定要更新吧
			//这也就是我们每找一个 最短边, 就要更新一次near的原因。 
			for(int j =1;j<=n;j++) {
				if(use[j]==0 && near[j]>map[t][j]) {  
					near[j] = map[t][j];
				}
			}
		}
		return res;
	}
	public static void main(String[] args) {
		Scanner sc=new Scanner(new BufferedInputStream(System.in));
		map = new int[ 200][200];
		near = new int [200];
		use =new int[200];
		while(true) {
			n=sc.nextInt();
			if(n==0)break;
			for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) {
				map[i][j]=inf;
			}
			for(int i=1;i<=((n*n-n)/2);i++) {
				int a =sc.nextInt();
				int b =sc.nextInt();
				int c = sc.nextInt();
				map[a][b]= map[b][a]=c;
			}
			System.out.println(prim());
		}
	}
}

kruskal 就拿这个为例子,poj1251

import java.io.BufferedInputStream;
import java.util.PriorityQueue;
import java.util.Scanner;


public class Main {
	
	static int n,m;
	static class e implements Comparable<e>{   //kruskal 存储边,并按距离从小到大存
		int u,v,w;
		public e(int u,int v,int w) {
			this.u=u;this.v=v;this.w=w;
		}
		@Override
		public int compareTo(e o) {
			return this.w-o.w;
		}
	};
	
	static int[] root,value;
	static PriorityQueue<e> es = new PriorityQueue<e>();

	static int find(int a) {  // find和union是固定记住的, 并查集的基本知识
		int temp;
		if(root[a]!=a) {
			temp = root[a];
			root[a]=find(root[a]);
			value[a] += value[temp];
		}
		return root[a];
	}
	static void union(int a,int b) {
		int roota=find(a);int rootb=find(b);
		if(roota!=rootb) {
			if(value[roota]>=value[rootb]) {
				root[rootb]=roota;
				if(value[roota]==value[rootb]) value[roota]++;
			}else {
				root[roota]=rootb;
			}
		}
	}
	static int  kruskal() { //克鲁斯卡尔最小生成树, 返回最小路径和
		int res=0;
		root=new int [100000]; for(int i=0;i<root.length;i++)root[i]=i;
		value = new int [100000]; 
		int con=0;
		//循环找n-1个边最小边, 很简单的想法
		//唯一注意的是,利用并查集判断是不是有环就ok了
		while(!es.isEmpty()) {   
			e  temp = es.poll();
			int rootbegin=find(temp.u);
			int rootend = find(temp.v);
			//意思是, 一旦加上这个边,就有环路了 ,这个边虽然小,但是不可用!
			if(rootbegin==rootend) {   
				continue;
			}
			else { //边可用
				res+=temp.w;
				union(temp.u , temp.v);
				con++; if(con==n-1) break;  //只用n-1个最小合法边
			}
		}
		return res;
	}
	public static void main(String[] args) {
		Scanner sc  = new Scanner(new BufferedInputStream(System.in));
		while(sc.hasNext()) {
			n=sc.nextInt(); es.clear();
			if(n==0) break;
			for(int i=1;i<=n-1;i++) {
				sc.next();
				m=sc.nextInt();
				for(int j=0;j<m;j++) {
					char c = sc.next().charAt(0);
					es.add(new e(i,c-'A'+1,sc.nextInt()));
				}
			}
			System.out.println(kruskal());
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值