BZOJ 1050 旅行

Description

给你一个无向图,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不可能相同。

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

HINT

【数据范围】

1<  N < = 500

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

0 < M < =5000

Source

自己YY了一个做法,感觉复杂度有点不太对,但跑起来还是飞快的。

枚举最大的边权,然后根据当前求出的最优答案找出最小边权,在这个范围内的边中做一遍最大生成树。先用并查集判断两点是否联通:不联通直接再见;否则再找出两点路径的最大值来更新答案。应该很好写,我都能想出来。

 1 #include<cstring>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstdio>
 5 #include<cstdlib>
 6 using namespace std;
 7
 8 #define inf (1<<30)
 9 #define maxn 510
10 #define maxm 5010
11
12 int n,m,up,down,side[maxn],toit[maxn*2],next[maxn*2];
13 int s,t,tot,cnt,father[maxn],bac[maxm],arr[maxn],len[maxn*2];
14 double ans = (double)inf; bool in[maxn];
15 struct EDGE
16 {
17     int x,y,v;
18     friend inline bool operator <(const EDGE &a,const EDGE &b) { return a.v > b.v; }
19 }edge[maxm],temp[maxm];
20
21 inline void init() { for (int i = 1;i <= n;++i) father[i] = i; }
22
23 inline int find(int a) { if (father[a]!=a) father[a] = find(father[a]); return father[a]; }
24
25 inline int gcd(int a,int b) { return b?gcd(b,a%b):a; }
26
27 inline void add(int a,int b,int c)
28 {
29     next[++cnt] = side[a]; side[a] = cnt;
30     toit[cnt] = b; len[cnt] = c;
31 }
32
33 inline void ins(int a,int b,int c) { add(a,b,c); add(b,a,c); }
34
35 inline int bfs()
36 {
37     int team[maxn],*head,*tail;
38     head = tail = team; memset(in,false,n+2);
39     *(++tail) = s; in[s] = true; arr[s] = inf;
40     while (head != tail)
41     {
42         int now = *(++head);
43         if (now == t) return arr[t];
44         for (int i = side[now];i;i = next[i])
45             if (!in[toit[i]]) *(++tail) = toit[i],arr[toit[i]] = min(arr[now],len[i]),in[toit[i]] = true;
46     }
47 }
48
49 inline void work(int key)
50 {
51     int lim = key/ans,all = 0,k = 0;
52     for (int i = 1;i <= m;++i) if (edge[i].v>lim&&edge[i].v <= key) temp[++all] = edge[i];
53     init(); memset(side,0,4*(n+2)); cnt = 0;
54     sort(temp+1,temp+all+1);
55     for (int i = 1;i <= all;++i)
56     {
57         int r1 = find(temp[i].x),r2 = find(temp[i].y);
58         if (r1 != r2)
59             father[r1] = r2,ins(temp[i].x,temp[i].y,temp[i].v),++k;
60         if (k == n-1) break;
61     }
62     if (find(s) != find(t)) return;
63     lim = bfs();
64     if ((1.0*key)/(1.0*lim) < ans) ans = (1.0*key)/(1.0*lim),up = key,down = lim;
65 }
66
67 int main()
68 {
69     freopen("1050.in","r",stdin);
70     freopen("1050.out","w",stdout);
71     scanf("%d %d",&n,&m);
72     init();
73     for (int i = 1;i <= m;++i)
74     {
75         int a,b,c; scanf("%d %d %d",&a,&b,&c);
76         edge[i] = (EDGE){a,b,c};
77         int r1 = find(a),r2 = find(b);
78         if (r1 != r2) father[r1] = r2;
79         bac[++tot] = c;
80     }
81     scanf("%d %d",&s,&t);
82     if (find(s) != find(t)) printf("IMPOSSIBLE\n"),exit(0);
83     sort(bac+1,bac+tot+1); tot = unique(bac+1,bac+tot+1)-bac-1;
84     for (int i = tot;i;--i) work(bac[i]);
85     int d = gcd(up,down);
86     up /= d; down /= d;
87     if (down != 1) printf("%d/%d",up,down);
88     else printf("%d",up);
89     fclose(stdin); fclose(stdout);
90     return 0;
91 }

时间: 2024-10-29 19:09:48

BZOJ 1050 旅行的相关文章

BZOJ 1050 旅行comf(枚举最小边-并查集)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1050 题意:给出一个带权图.求一条s到t的路径使得这条路径上最大最小边的比值最小? 思路:将边排序.枚举最小边,然后将边一个一个插到并查集里,s和t联通时计算更新答案. struct node { int u,v,w; void get() { RD(u,v,w); } }; int cmp(node a,node b) { return a.w<b.w; } int n,m,s,t;

BZOJ 1050 旅行(并查集)

很好的一道题. 首先注意,要使的s到t的路径上最大边/最小边的值最小.我们可以尝试一下二分并验证答案. 但是不能二分最大边/最小边. 我们可以二分 最小边的权值.于是算法就出来了. 首先把边权排序.然后枚举最小的边,再依次添加不小于该边的边,直到s和t联通.用并查集维护即可. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include &l

[BZOJ]1050 旅行comf(HAOI2006)

图论一直是小C的弱项,相比其它题型,图论的花样通常会更多一点,套路也更难捉摸. Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求一条路径,使得路径上最大边和最小边的比值最小.如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出这个比值,如果需要,表示成一个既约分数. 备注: 两个顶点之间可能有多条路径. Input 第一行包含两个正整数,N和M.下来的M行每行包含三个正

BZOJ 1050 旅行comf

题目如下: 题目描述 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求一条路径,使得路径上最大边和最小边的比值最小.如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出这个比值,如果需要,表示成一个既约分数. 备注: 两个顶点之间可能有多条路径. 输入 第一行包含两个正整数,N和M. 下来的M行每行包含三个正整数:x,y和v.表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶

bzoj 1050: [HAOI2006]旅行comf(最小生成树+并查集)

1050: [HAOI2006]旅行comf Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 2405  Solved: 1282 [Submit][Status][Discuss] Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T ,求一条路径,使得路径上最大边和最小边的比值最小.如果S和T之间没有路径,输出"IMPOSS

BZOJ 1050: [HAOI2006]旅行comf(枚举+并查集)

[HAOI2006]旅行comf Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求一条路径,使得路径上最大边和最小边的比值最小.如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出这个比值,如果需要,表示成一个既约分数. 备注: 两个顶点之间可能有多条路径. Input 第一行包含两个正整数,N和M.下来的M行每行包含三个正整数:x,y和v.表示景点x到景点y之间有一条

bzoj 1050 [HAOI2006]旅行comf

题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1050 思路: 先将每条边的权值排个序优先小的,然后从小到大枚举每一条边,将其存到并查集里,如果得到的比值比之前的小,那么判断下s与t能否连通,如果连通就替换就好了 实现代码: #include<bits/stdc++.h> using namespace std; const int M = 1e6+10; int f[M],vis[M],a[M]; int n,m; int Fi

bzoj 1050: [HAOI2006]旅行comf&amp;&amp;【codevs1001】

Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求 一条路径,使得路径上最大边和最小边的比值最小.如果S和T之间没有路径,输出"IMPOSSIBLE",否则输出这个 比值,如果需要,表示成一个既约分数. 备注: 两个顶点之间可能有多条路径. Input 第一行包含两个正整数,N和M.下来的M行每行包含三个正整数:x,y和v.表示景点x到景点y之间有一条双向公路

BZOJ 1050 HAOI2006 旅行comf 动点SPFA

题目大意:给定一个无向图,每条边上有权值,求起点到终点的路径中最长边和最短边的最小比值 随手点开一道居然是动点SPFA的裸题-- 魔法森林都切了这个问题就不大了 我们把边权排序,从大到小加进这个图中,每加进一条边就把边的两个端点加进队列,直接跑SPFA,维护起点到每个点路径上的最长边的最小值,然后用当前边权作为分母更新ans 这样可以保证每次跑出来的都是当前边为最短边时起点到终点的最长边的最小值,同时由于我们保留了上一次的信息而不至于超时 好强大的算法--可惜搞不出来时间复杂度0.0 #incl