poj1251 最小生成树

问题重述

问题描述:
Lagrishan的一个热带岛屿上的行政长官有一个问题要解决。他决定把几年前得到的外国援助资金用于修建村庄之间的道路。但是丛林比道路多太多了,使道路网络的维护太过于昂贵了。理事会必须选择停止维修一些道路。上述左侧图显示当前所有使用中的道路,以及现在每月的维护费用。当然,村庄之间必需有一些公路能够相通,即使路线并不像以前一样短。行政长官想告诉理事会怎样才使每月的花费最小,并且所维持的道路,将连接所有村庄。上面的地图标记了村庄A到I。右边的图显示了每月能够维护道路的最小费用为216aacms。你的任务是编写一个程序,将解决这些问题。
输入:
输入包含的数据集个数在100以内,以0作为最后一行。每个数据集的第一行只包含一个表示村庄个数的数n,1<n<27,并且这n个村庄是由大写字母表里的前n个字母表示。接下来的n- 1行是由字母表的前n-1个字母开头。最后一个村庄表示的字母不用输入。对于每一行,以每个村庄表示的字母开头,然后后面跟着一个数字,表示有多少条道路可以从这个村到后面字母表中的村庄。如果k是大于0,表示该行后面会表示k条道路的k个数据。每条道路的数据是由表示连接到另一端村庄的字母和每月维修该道路的花费组成。维修费用是正整数的并且小于100。该行的所有数据字段分隔单一空白。该公路网将始终连接所有的村庄。该公路网将永远不会超过75条道路。没有任何一个村庄会有超过15条的道路连接到其他村庄(之前或之后的字母)。在下面的示例输入,其中第一个数据集是与上面的地图相一致的。
输出:
输出是每行一个整数,表示每个数据集中每月维持道路系统连接到所有村庄所花费的最低成本。Caution: A brute force solution that examines every possible set of roads will not finish within the one minute time limit. 注意:这是一种强力的解决方案,不是每一个数据集的所生成的道路网都能在规定的1分钟之内的时间完成。

Sample Input

9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0
Sample Output

216
30
解题过程:
分析过程:
从题目中的图很容易就可以看出这是一道最小生成树的问题,对于最小生成树,有两种算法可以解决。一种是Prim算法,该算法的时间复杂度为O(n2),与图中边数无关,该算法适合于稠密图,而另外一种是 Kruskal,该算法的时间主要取决于边数,它较适合于稀疏图。再看下题目其顶点最多26个,边最多75条。图是稀疏还是稠密还得取决于具体输入,由题目的背景可知,这道题给出的图一定是连通的无向图,接下来,考虑下数据的输入与存储处理,很显然,题目输入的是一些带有权值的边,所以我打算直接定义一个边类来存储,这样的话,我用Kruskal算法的话,就相对方便点。只要对输入的边的权值排一次序,逐个边判断,只要边的两端不在同一棵树中,就可以加入,找够n-1条边就行了。最后最小生成树的所有权值相加就可以输出了。
编程过程:
代码的编写还算是比较顺利。感觉比上一道的拓扑排序好多了,代码也相对简洁明了。这道题有一个地方容易忽漏出错,我刚开始只是简单判断新得到的边的两端是不是已经在生成树中存在了,漏掉了一种情况:在生成最小生成树中,有可能会先产生多棵树,最后才生成一棵树。当考虑到这种情况后,随之而来,产生了这道题的一个难点,如何去判断两个端点分别在两棵树中。最后还是写出了代码,但当我测试通过了后,上交却是runtime error,但我却不知道是哪里错了。之后,跟同学要大量的测试数据,但是没问题。最后听同学的建议,再去查看输入的地方,然后我就换另外一种表达方式,终于过了。
做题收获:
做了这道题,使我对runtime error,不会太恐惧了。之前做过两道也是runtime error,弄了很久,现在已经有一些经验了。更重要的一点是,通过这道题,我找到了另外一种不容易出错的输入处理方式。
程序源码:
package middle;


import java.io.BufferedInputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Scanner;


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
* poj1251
* 终于过了,以后还是不要用nextLine了,用这个再去spilt很容易就错了的。
* 之前做过一道题,出错是因为nextLine和nextInt混用了。
* 我把它拿到zoj去上交时,却编译不过。最后才去掉所有注释才过了。
* @author NC
*/
public class Poj1251 {

public static void main(String[] args) {
Scanner scanner = new Scanner(new BufferedInputStream(System.in));
while (scanner.hasNext()) {
int n = scanner.nextInt();
if (n == 0) {
break;
}
String a;
LinkedList<Edge> graph = new LinkedList();
for (int i = 0; i < n - 1; i++) {
a = scanner.next("[A-Z]");
int m = scanner.nextInt();
int j = 0;
while (j < m) {
String b = scanner.next("[A-Z]");
int value = scanner.nextInt();
graph.add(new Edge(a, b, value));
j++;
}
}
LinkedList<Edge> l = Kruskal(graph);
Iterator<Edge> ite = l.iterator();
Integer sum = 0;
while (ite.hasNext()) {
Edge e = ite.next();
sum = sum + e.getValue();
}
System.out.println(sum);
}
}

static LinkedList<Edge> Kruskal(LinkedList graph) {
Collections.sort(graph);
LinkedList<Edge> g = new LinkedList();
HashMap<String, Integer> vertexes = new HashMap<String, Integer>();
Iterator<Edge> it = graph.iterator();
Integer tree = 0;
while (it.hasNext()) {
Edge e = it.next();
if (g.size() == 0) {
g.add(e);
vertexes.put(e.getVertex1(), tree);
vertexes.put(e.getVertex2(), tree);
} else {
if (!vertexes.containsKey(e.getVertex1()) && vertexes.containsKey(e.getVertex2())) {
//有一个顶点已经添加过,直接把边加进去
g.add(e);
vertexes.put(e.getVertex1(), vertexes.get(e.getVertex2()));

} else if (vertexes.containsKey(e.getVertex1()) && !vertexes.containsKey(e.getVertex2())) {
//有一个顶点已经添加过,直接把边加进去
g.add(e);
vertexes.put(e.getVertex2(), vertexes.get(e.getVertex1()));
} else if (!vertexes.containsKey(e.getVertex1()) && !vertexes.containsKey(e.getVertex2())) {
//两个顶点都没有增加过,故增加一棵树
g.add(e);
tree++;
vertexes.put(e.getVertex1(), tree);
vertexes.put(e.getVertex2(), tree);
} else if (vertexes.get(e.getVertex1()) != vertexes.get(e.getVertex2())) {
//两个顶点都加过了,如果不是同一棵树的话
g.add(e);
//合并两棵树
vertexes = MergeTwoTrees(vertexes, e.getVertex1(), e.getVertex2());
}
}
}
return g;
}

static HashMap<String, Integer> MergeTwoTrees(HashMap<String, Integer> vertexes, String v1, String v2) {
HashMap<String, Integer> hm = new HashMap<String, Integer>();
Integer a = vertexes.get(v1);
Integer b = vertexes.get(v2);
Iterator it = vertexes.entrySet().iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
String key = (String) entry.getKey();
Integer value = (Integer) entry.getValue();
if (b == value) {
value = a;
}
hm.put(key, value);
}
return hm;
}
}

class Edge implements Comparable {

private String vertex1;
private String vertex2;
private Integer value;

public Edge(String vertex1, String vertex2, Integer value) {
this.vertex1 = vertex1;
this.vertex2 = vertex2;
this.value = value;
}

public int getValue() {
return value;
}

public void setValue(int value) {
this.value = value;
}

public String getVertex1() {
return vertex1;
}

public void setVertex1(String vertex1) {
this.vertex1 = vertex1;
}

public String getVertex2() {
return vertex2;
}

public void setVertex2(String vertex2) {
this.vertex2 = vertex2;
}

public int compareTo(Object o) {
Edge e = (Edge) o;
return this.value - e.getValue();
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值