1. 程式人生 > >P2502:[HAOI2006]旅行

P2502:[HAOI2006]旅行

最大 最後一行 mark pan 車輛 opera 路徑 haoi2006 unit

題目描述

Z小鎮是一個景色宜人的地方,吸引來自各地的觀光客來此旅遊觀光。Z小鎮附近共有N個景點(編號為1,2,3,…,N),這些景點被M條道路連接著,所有道路都是雙向的,兩個景點之間可能有多條道路。也許是為了保護該地的旅遊資源,Z小鎮有個奇怪的規定,就是對於一條給定的公路Ri,任何在該公路上行駛的車輛速度必須為Vi。速度變化太快使得遊客們很不舒服,因此從一個景點前往另一個景點的時候,大家都希望選擇行使過程中最大速度和最小速度的比盡可能小的路線,也就是所謂最舒適的路線。

輸入輸出格式

輸入格式:
第一行包含兩個正整數,N和M。

接下來的M行每行包含三個正整數:x,y和v。表示景點x到景點y之間有一條雙向公路,車輛必須以速度v在該公路上行駛。

最後一行包含兩個正整數s,t,表示想知道從景點s到景點t最大最小速度比最小的路徑。s和t不可能相同。

輸出格式:
如果景點s到景點t沒有路徑,輸出“IMPOSSIBLE”。否則輸出一個數,表示最小的速度比。如果需要,輸出一個既約分數。

輸入輸出樣例

輸入樣例#1:
4 2
1 2 1
3 4 2
1 4
輸出樣例#1:
IMPOSSIBLE

輸入樣例#2:
3 3
1 2 10
1 2 5
2 3 8
1 3
輸出樣例#2:
5/4

輸入樣例#3:
3 2
1 2 2
2 3 4
1 3
輸出樣例#3:
2

說明

【數據範圍】

1<N≤500

1≤x,y≤N,0<v<30000,x≠y

0<M≤5000


題解

咋一看挺唬人的一道題(哎,圖論總是我的痛……)
不過看看n、m這麽小,可以想到把邊權(Vi)從小到大排個序,從某條小邊開始不斷加邊,直到s與t連通
如何判斷是否連通呢——並查集
這樣每次把“小邊”往後推,再加邊,s與t連通後邊權差用最後加的那條邊的邊權-小邊邊權即可
由於並查集不支持刪點,所以每次只能將小邊往後推一個

這種想法很巧啊~


代碼

#include<cstdio>
#include<iostream>
#include<algorithm>

#define INF 30000005
using namespace std;

const int N = 505
; const int M = 5005; struct edge{ int u,v,len; bool operator < (const edge &x) const{ return x.len>len; } }h[M]; int fa[N]; int Getfa(int x){ return fa[x]==x?x:fa[x]=Getfa(fa[x]); } void unit(int x,int y) { fa[Getfa(x)]=Getfa(y); } int n,m,s,t; int up,down=1; double ans=INF; int prime[5000],p[30005],num; void Getprime(){ for(int i=2;i<30005;i++) p[i]=1; for(int i=2;i<30005;i++){ if(p[i]) prime[num++]=i; for(int j=0;j<num && (long long)prime[j]*i<30005;j++){ p[prime[j]*i]=0; if(i%prime[j]==0) break; } } } int main() { int i,j; scanf("%d%d",&n,&m); for(i=0;i<m;i++) scanf("%d%d%d",&h[i].u,&h[i].v,&h[i].len); scanf("%d%d",&s,&t); sort(h,h+m); for(i=0;i<m;i++){ for(j=1;j<=n;j++) fa[j]=j; for(j=i;j<m;j++){ unit(h[j].u,h[j].v); if(Getfa(s)==Getfa(t)) break; } if(Getfa(s)==Getfa(t) && ((double)h[j].len/h[i].len)<ans) { up=h[j].len; down=h[i].len; ans=((double)up/down); } } if(ans==INF) printf("IMPOSSIBLE\n"); else if((int)ans*down==up) printf("%.0lf\n",ans); else { Getprime(); for(i=0;i<num && prime[i]<down;i++) while(up%prime[i]==0 && down%prime[i]==0) up/=prime[i],down/=prime[i]; printf("%d/%d\n",up,down); } return 0; }

P2502:[HAOI2006]旅行