POJ 3259:Wormholes
Description
John农夫拥有许多农场,John发现了许多神奇的树洞。这些树洞 都非常特殊因为进入树洞后能够把你送到一个目的地,并且到达目的地时的时间早于你进入树洞时的时间(比如你进入洞时是2000年,但是出洞时发现是1990年)。每个农场都有N(1<=N<=500)个field,记为1...N,M(1<=M<=2500)条path,W(1<=W<=200)个wormhole。
John是一个很喜欢穿越的人,他想从某个区域进入,并且经过一系列的路径或树洞 ,最后到达起点,并且到达起点时发现比最初在起点的时间还早。
为了帮助John实现这个愿望,他提供给我们他的F(1<=F<=5)个farm的地图。已知穿过每一条path的时间都小于等于10000s,穿过每个树洞所倒退的时间小于等于10000s。
Input
第一行是farm的个数。
第二行是第一个farm的field个数N,path的个数M,树洞的个数W。
第3~M+2 行是 path 的起点、终点、穿越时间(注意,这里的path都是双向边)
第M+3~M+W+2 行是树洞的起点、终点、倒退时间(单向边)
接着是第二个farm。
Output
对于每个farm,如果这个farm能够达成目标,则输出“YES”,否则输出“NO"。
Sample Input
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
Sample Output
NO
YES
Hint
对于 Sample Input :
farm 1 不能够达到目标。
farm 2 能够,因为 1->2->3->1,他回到1时比原来提前了1秒。
算法思想
将field作为节点,path作为正权边,wormholes作为负权边,注意:path是双向边。
第一步:添加一个虚拟源点s,并将这个虚拟源点连向所有的节点(field),因此共有N+1个节点,2*M+W+N条边。
第二步:对虚拟源点s运行Bellman-Ford算法,判断是否有源点可达负圈即可。
算法实现
1 2 3 import java.util.Scanner; 4 5 public class Main { 6 int n; // 节点个数 7 int m; // 边的个数 8 int head[]; // head[i] 表示节点i指向的第一条边的索引是多少 9 int edge[][]; // edge[i]表示第i条边 ,edge[i][1]表示这条边的起点,edge[i][2]表示这条边的终点,edge[i][3]表示这条边的权重 10 int next[]; // next[i]表示第i条边在邻接表中的下一条边是什么 11 int d[]; // d[i]表示第i条边的d值 12 13 int farmNumber; 14 int N[]; 15 int M[]; 16 int W[]; 17 int current_farm = 0; 18 19 public void getSingleFarm(Scanner in){ 20 int current_edge = 0; 21 N[current_farm] = in.nextInt(); 22 M[current_farm] = in.nextInt(); 23 W[current_farm] = in.nextInt(); 24 this.n = N[current_farm]+1; 25 this.m = 2*M[current_farm]+W[current_farm]+N[current_farm]; 26 //2*M+W+N条边 27 head = new int[n]; 28 edge = new int[m][4]; 29 next = new int[m]; 30 d = new int[n]; 31 for(int i=0;i<n;i++){ 32 head[i] = -1; 33 } 34 for(int i=1;i<=N[current_farm];i++){ 35 edge[current_edge][1] = 0; 36 edge[current_edge][2] = i; 37 edge[current_edge][3] = 0; 38 next[current_edge] = head[0]; 39 head[0] = current_edge; 40 current_edge++; 41 } 42 for(int i=1;i<=M[current_farm];i++){ 43 int from = in.nextInt(); 44 int to = in.nextInt(); 45 int cost = in.nextInt(); 46 edge[current_edge][1] = from; 47 edge[current_edge][2] = to; 48 edge[current_edge][3] = cost; 49 next[current_edge] = head[from]; 50 head[from] = current_edge; 51 current_edge++; 52 53 edge[current_edge][1] = to; 54 edge[current_edge][2] = from; 55 edge[current_edge][3] = cost; 56 next[current_edge] = head[to]; 57 head[to] = current_edge; 58 current_edge++; 59 } 60 for(int i=1;i<=W[current_farm];i++){ 61 int from = in.nextInt(); 62 int to = in.nextInt(); 63 int cost = in.nextInt(); 64 edge[current_edge][1] = from; 65 edge[current_edge][2] = to; 66 edge[current_edge][3] = -cost; 67 next[current_edge] = head[from]; 68 head[from] = current_edge; 69 current_edge++; 70 } 71 current_farm++; 72 } 73 public void getInput(Scanner in){ 74 75 farmNumber = in.nextInt(); 76 N = new int[farmNumber]; 77 M = new int[farmNumber]; 78 W = new int[farmNumber]; 79 80 for(int i=1;i<=farmNumber;i++){ 81 getSingleFarm(in); 82 boolean flag = BellmanFord(0); 83 if(flag) System.out.println("NO"); 84 else System.out.println("YES"); 85 86 } 87 } 88 89 public boolean BellmanFord(int s){ 90 for(int i=0;i<n;i++){ 91 d[i] = Integer.MAX_VALUE; 92 } 93 d[s] = 0; 94 boolean flag = true; 95 for(int t=1;t<=n-1;t++){ 96 if(flag){ 97 flag = false; 98 for(int i=0;i<n;i++){ 99 for(int j=head[i];j!=-1;j=next[j]){ 100 if(d[edge[j][2]]>d[i]+edge[j][3]){ 101 d[edge[j][2]] = d[i] + edge[j][3]; 102 flag = true; 103 } 104 } 105 } 106 } 107 } 108 for(int i=0;i<n;i++){ 109 for(int j=head[i];j!=-1;j=next[j]){ 110 if(d[edge[j][2]]>d[i]+edge[j][3]){ 111 return false; 112 } 113 } 114 } 115 return true; 116 } 117 118 public static void main(String[] args) { 119 Main main = new Main(); 120 main.getInput(new Scanner(System.in)); 121 122 } 123 }