1. 程式人生 > >BZOJ1050 旅行comf(kruskal)

BZOJ1050 旅行comf(kruskal)

eof 速度 lose 代碼 min main imp int 不可

旅行comf

  給你一個無向圖,N(N<=500)個頂點, M(M<=5000)條邊,每條邊有一個權值Vi(Vi<30000)。給你兩個頂點S和T,求一條路徑,使得路徑上最大邊和最小邊的比值最小。如果S和T之間沒有路徑,輸出”IMPOSSIBLE”,否則輸出這個比值,如果需要,表示成一個既約分數。 備註: 兩個頂點之間可能有多條路徑。 Input   第一行包含兩個正整數,N和M。下來的M行每行包含三個正整數:x,y和v。表示景點x到景點y之間有一條雙向公路,車輛必須以速度v在該公路上行駛。最後一行包含兩個正整數s,t,表示想知道從景點s到景點t最大最小速度比最小的路徑。s和t不可能相同。1<N<=500,1<=x,y<=N,0<v<30000,0<M<=5000 Output   如果景點s到景點t沒有路徑,輸出“IMPOSSIBLE”。否則輸出一個數,表示最小的速度比。   如果需要,輸出一個既約分數。 Sample Input 【樣例輸入1】
  4 2   1 2 1   3 4 2   1 4 【樣例輸入2】   3 3   1 2 10   1 2 5   2 3 8   1 3 【樣例輸入3】   3 2   1 2 2   2 3 4   1 3

Sample Output

【樣例輸出1】

  IMPOSSIBLE

【樣例輸出2】

  5/4

【樣例輸出3】

  2

解題思路:
  本題給出景點數量n,道路數量m,之後給出m行,每行包括三個整數,分別為道路兩端的景點,x,y與該道路的行駛速度v,下一行為兩個整數,分別為起始景點s,目標景點t,如果s與t之間連通,就選擇一條道路使從s到達t的最小速度比最大,輸出其最大邊和最小邊的比值的最小值的最簡分數形式,若不連通,輸出IMPOSSIBLE。

  本題最小速度比是指一條道路上最大的速度與最小的速度的比值最小。且我們首先思考如何找到一條連通s與t道路上的最大邊與最小邊,怎麽辦?最小生成樹!克魯斯卡爾!

kruskal算法核心思想:  

  既然已經給出了鄰接表。將道路按速度由小到大排序,枚舉最小速度,初始視所有景點都為不連通,之後由最小速度的道路開始以速度從小到大枚舉所有道路,判斷道路兩端的景點是否已經連通,若已經連通不做處理,若不連通則將該道路記錄入最小生成樹,標記道路兩端為連通,判斷s與t是否連通,若連通標記s與t可達,計算此時的最大速度與最小速度比值(因為道路速度由小到大遍歷所以最小速度為開始遍歷時的道路速度,最大速度為當前道路速度),將現在的比值與之前記錄的比值比較,如果現在的值小於先前的值,將比值記錄為現在的比值,並記錄這時的最大速度與最小速度。

技術分享圖片
bool kruskal(int n, int m, int s, int t, int &maxL, int &minL){
    //傳入的n為景點數量,m為道路數量,s為起點,t為目標景點
    //由於要改變maxL與minL記錄最終的最大值與最小值,所以maxL與minL傳引用
    double ans = inf;   //初始化比值為無窮大
    bool flag = false;  //標記s與t為不可達
    sort(Edge + 1, Edge + 1 + m, cmp);  //將道路由小到大排序
    for(int i = 1; i <= m; i++){    //枚舉最小速度
        for(int k = 1; k <= n; k++){    //初始化所有景點為不連通
            father[k] = k;
        }
        int minlen = Edge[i].v, maxlen = 0; //記錄最小速度,初始化最大速度為0
        for(int j = i; j <= m; j++){    //以速度由小到大枚舉所有道路
            int faNode1 = getFather(Edge[j].node1);
            int faNode2 = getFather(Edge[j].node2);
            maxlen = Edge[j].v; //記錄最大速度為當前道路速度
            if(faNode1 != faNode2){ //判斷道路兩端頂點是否連通
                father[faNode1] = faNode2;  //不連通就標記為連通
                if(getFather(s) == getFather(t)){   //判斷s與t是否連通
                    flag = true;    //如果連通標記s與t為可達
                    if(ans > (double)maxlen / minlen){  
            //計算此時的最大速度與最小速度比值,將現在的比值與之前記錄的比值比較
                        ans = (double)maxlen / minlen;
                    //如果現在的值小於先前的值,將比值記錄為現在的比值
                        maxL = maxlen;
                        minL = minlen;
                        //記錄這時的最大速度與最小速度
                        break;
                    }
                }
            }
        }
    }
    if(flag){   //如果s與t可達返回true,否則返回false
        return true;
    }else{
        return false;
    }
}
kruskal

是否連通用並查集進行判斷

  

技術分享圖片
int father[maxn], maxL, minL;   //並查集部分
int getFather(int x)
{
    if(father[x] == x)
        return x;
    else
        return father[x] = getFather(father[x]);
}
並查集

AC代碼

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int inf = 0x7fffffff; //無窮大
 4 const int maxn = 3e4+10;
 5 struct edge{    //edge保存道路
 6     int node1, node2;   //道路兩端景點
 7     int v;  //道路速度
 8 }Edge[maxn];
 9 int gcd(int a, int b){  //計算最大公約數
10     if(b == 0)
11         return a;
12     else
13         return gcd( b, a % b);
14 }
15 bool cmp(edge e1, edge e2){ //道路按速度由小到大排序
16     return e1.v < e2.v;
17 }
18 int father[maxn], maxL, minL;   //並查集部分
19 int getFather(int x)
20 {
21     if(father[x] == x)
22         return x;
23     else
24         return father[x] = getFather(father[x]);
25 }
26 bool kruskal(int n, int m, int s, int t, int &maxL, int &minL){
27     //傳入的n為景點數量,m為道路數量,s為起點,t為目標景點
28     //由於要改變maxL與minL記錄最終的最大值與最小值,所以maxL與minL傳引用
29     double ans = inf;   //初始化比值為無窮大
30     bool flag = false;  //標記s與t為不可達
31     sort(Edge + 1, Edge + 1 + m, cmp);  //將道路由小到大排序
32     for(int i = 1; i <= m; i++){    //枚舉最小速度
33         for(int k = 1; k <= n; k++){    //初始化所有景點為不連通
34             father[k] = k;
35         }
36         int minlen = Edge[i].v, maxlen = 0; //記錄最小速度,初始化最大速度為0
37         for(int j = i; j <= m; j++){    //以速度由小到大枚舉所有道路
38             int faNode1 = getFather(Edge[j].node1);
39             int faNode2 = getFather(Edge[j].node2);
40             maxlen = Edge[j].v; //記錄最大速度為當前道路速度
41             if(faNode1 != faNode2){ //判斷道路兩端頂點是否連通
42                 father[faNode1] = faNode2;  //不連通就標記為連通
43                 if(getFather(s) == getFather(t)){   //判斷s與t是否連通
44                     flag = true;    //如果連通標記s與t為可達
45                     if(ans > (double)maxlen / minlen){  
46             //計算此時的最大速度與最小速度比值,將現在的比值與之前記錄的比值比較
47                         ans = (double)maxlen / minlen;
48                     //如果現在的值小於先前的值,將比值記錄為現在的比值
49                         maxL = maxlen;
50                         minL = minlen;
51                         //記錄這時的最大速度與最小速度
52                         break;
53                     }
54                 }
55             }
56         }
57     }
58     if(flag){   //如果s與t可達返回true,否則返回false
59         return true;
60     }else{
61         return false;
62     }
63 }
64 int main()
65 {
66     int numNode, numEdge;   
67     while(scanf("%d%d", &numNode, &numEdge)!= EOF){ //輸入景點數與道路數
68         for(int i = 1; i <= numEdge; i++){  //輸入道路
69             scanf("%d%d%d", &Edge[i].node1, &Edge[i].node2, &Edge[i].cost);
70         }
71         int s, t;
72         scanf("%d%d", &s, &t);  //輸入起點終點
73         int maxL  = 0;
74         int minL = 0;
75         //初始化最大最小速度都為0
76         if(kruskal(numNode, numEdge, s, t, maxL, minL)){    //如果s與t連通
77             int temp = gcd(maxL, minL); //計算最大最小速度的最大公約數
78             //printf("%d\n", gcd(maxL, minL));
79             int a = maxL/temp, b = minL/temp;   //計算分子分母
80             if(b != 1)  //分子不等於1輸出最簡分數
81                 printf("%d/%d\n", a, b);
82             else
83                 printf("%d\n", a);  //分子為1直接輸出分母
84         }else{  //不連通輸出IMPOSSIBLE
85             printf("IMPOSSIBLE\n");
86         }
87     }
88     return 0;
89 }

BZOJ1050 旅行comf(kruskal)