编程之美2013全国挑战赛资格赛第3题

来源:互联网 发布:sql 分组求和汇总 编辑:程序博客网 时间:2024/05/22 11:44

时间限制: 2000ms 内存限制: 256MB

描述

有一棵树,树上有只毛毛虫。它在这棵树上生活了很久,对它的构造了如指掌。所以它在树上从来都是走最短路,不会绕路。它还还特别喜欢三角形,所以当它在树上爬来爬去的时候总会在想,如果把刚才爬过的那几根树枝/树干锯下来,能不能从中选三根出来拼成一个三角形呢?

输入

输入数据的第一行包含一个整数 T,表示数据组数。

接下来有 T 组数据,每组数据中:

第一行包含一个整数 N,表示树上节点的个数(从 1 到 N 标号)。

接下来的 N-1 行包含三个整数 a, b, len,表示有一根长度为 len 的树枝/树干在节点 a 和节点 b 之间。

接下来一行包含一个整数 M,表示询问数。

接下来M行每行两个整数 S, T,表示毛毛虫从 S 爬行到了 T,询问这段路程中的树枝/树干是否能拼成三角形。

输出

对于每组数据,先输出一行"Case #X:",其中X为数据组数编号,从 1 开始。

接下来对于每个询问输出一行,包含"Yes"或“No”,表示是否可以拼成三角形。

数据范围

1 ≤ T ≤ 5

小数据:1 ≤ N ≤ 100, 1 ≤ M ≤ 100, 1 ≤ len ≤ 10000

大数据:1 ≤ N ≤ 100000, 1 ≤ M ≤ 100000, 1 ≤ len ≤ 1000000000

样例输入
251 2 51 3 202 4 304 5 1523 43 551 4 322 3 1003 5 454 5 6021 41 3
样例输出
Case #1:NoYesCase #2:NoYes
java源码:

import java.util.Scanner;
import java.util.Deque;
import java.util.LinkedList;


/**
 * 编程之美2013全国挑战赛资格赛第3题。
 * @author Christopher
 *
 */
public class Main {
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int T = in.nextInt();
String[] result = new String[T];
for(int i = 0; i < T; i++){
int N = in.nextInt();//边数
Edge[] edges = new Edge[N-1];
//获取该树的边的信息
for(int j = 0; j < edges.length; j++){
int n1 = in.nextInt();
int n2 = in.nextInt();
long len = in.nextLong();
edges[j] = new Edge(n1, n2, len);
}

int M = in.nextInt();//询问数
result[i] = "Case #" + (i+1) + ":\n";
for(int j = 0; j < M; j++){
int from = in.nextInt();//起点
int to = in.nextInt();//终点
result[i] += canMakeTriangle(from, to, edges) ? "Yes\n" : "No\n";
}
}

//输出结果
for(int i = 0; i < result.length; i++){
System.out.print(result[i]);
}
}

/**
* 判断从节点from到节点to之间经过的边中,是否存在三条边可以组成一个三角形。
* @param from
* @param to
* @param edges
* @return
*/
private static boolean canMakeTriangle(int from, int to, Edge[] edges){
//查找从from到to经过的所有边
Edge[] allEdges = findPath(from, to, edges);

/*判断是否有三条边可以组成一个三角形*/
if(allEdges.length < 3) return false;
//按照边的长度进行排序
java.util.Arrays.sort(allEdges, new java.util.Comparator<Edge>(){
public int compare(Edge e1, Edge e2){
if(e1.length > e2.length) return 1;
else if(e1.length == e2.length) return 0;
else return -1;
}
});
//深度遍历,蛮力法判断
for(int i = 0; i < allEdges.length - 2; i++){
forj:
for(int j = i+1; j < allEdges.length - 1; j++){
for(int k = j+1; k < allEdges.length; k++){
if(allEdges[i].length + allEdges[j].length > allEdges[k].length)
return true;
else continue forj;
}
}
}
return false;
}

/**
* 查找从节点from到节点to之间的路径,依次存入数组中并返回。
* @param from
* @param to
* @param edges
* @return
*/
private static Edge[] findPath(int from, int to, Edge[] edges){
Deque<Edge> path = new LinkedList<Edge>();
return findPath(from, to, edges, path);
}
private static Edge[] findPath(int from, int to, Edge[] edges, Deque<Edge> currentPath){
if(from == to){//已经找到路径
return currentPath.toArray(new Edge[0]);
}
else{
for(int i = 0; i < edges.length; i++){
int nextFrom = 0;
if(edges[i].node1 == from){
nextFrom = edges[i].node2;
}
else if(edges[i].node2 == from){
nextFrom = edges[i].node1;
}
if(nextFrom != 0 && !currentPath.contains(edges[i])){
currentPath.push(edges[i]);
Edge[] result = null;
result = findPath(nextFrom, to, edges, currentPath);
if(result == null){
currentPath.pop();
continue;
}
else return result;
}
}
return null;
}
}

/**
* 该类提供对题目中树干(即:边)的抽象表示。
* @author Christopher
*
*/
static class Edge{
int node1, node2;//两个端点
long length;//长度
public Edge(int n1, int n2, long len){
node1 = n1;
node2 = n2;
length = len;
}

public boolean equals(Object otherEdge){
if(otherEdge == null) return false;
if(this == otherEdge) return true;
if(otherEdge == null) return false;
if(this.getClass() != otherEdge.getClass()) return false;
Edge other = (Edge) otherEdge;
return (this.node1 == other.node1 && this.node2 == other.node2) ||
(this.node1 == other.node2 && this.node2 == other.node1);
}

public int hashCode(){
return String.valueOf(node1).hashCode() + String.valueOf(node2).hashCode();
}

}


}