最小生成树的题目,不知为何,几个题目算是做的比较顺畅,为防遗忘,再次做出总结
基本上两个思考方式,先贴出自己的模板,再进行解释
基本上两个套路,
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());
}
}
}