分类:图论,生成树,最短路,并查集
作者:ACShiryu
时间:2011-7-28
地址:ACShiryu's Blog
Heavy Transportation
Time Limit: 3000MS | Memory Limit: 30000K | |
Total Submissions: 11929 | Accepted: 3171 |
Description
Background
Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man who tells him whether there really is a way from the place his customer has build his giant steel crane to the place where it is needed on which all streets can carry the weight.
Fortunately he already has a plan of the city with all streets and bridges and all the allowed weights.Unfortunately he has no idea how to find the the maximum weight capacity in order to tell his customer how heavy the crane may become. But you surely know.
Problem
You are given the plan of the city, described by the streets (with weight limits) between the crossings, which are numbered from 1 to n. Your task is to find the maximum weight that can be transported from crossing 1 (Hugo's place) to crossing n (the customer's place). You may assume that there is at least one path. All streets can be travelled in both directions.
Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man who tells him whether there really is a way from the place his customer has build his giant steel crane to the place where it is needed on which all streets can carry the weight.
Fortunately he already has a plan of the city with all streets and bridges and all the allowed weights.Unfortunately he has no idea how to find the the maximum weight capacity in order to tell his customer how heavy the crane may become. But you surely know.
Problem
You are given the plan of the city, described by the streets (with weight limits) between the crossings, which are numbered from 1 to n. Your task is to find the maximum weight that can be transported from crossing 1 (Hugo's place) to crossing n (the customer's place). You may assume that there is at least one path. All streets can be travelled in both directions.
Input
The first line contains the number of scenarios (city plans). For each city the number n of street crossings (1 <= n <= 1000) and number m of streets are given on the first line. The following m lines contain triples of integers specifying start and end crossing of the street and the maximum allowed weight, which is positive and not larger than 1000000. There will be at most one street between each pair of crossings.
Output
The output for every scenario begins with a line containing "Scenario #i:", where i is the number of the scenario starting at 1. Then print a single line containing the maximum allowed weight that Hugo can transport to the customer. Terminate the output for the scenario with a blank line.
Sample Input
1 3 3 1 2 3 1 3 4 2 3 5
Sample Output
Scenario #1: 4
题目大意是就是何处一个图,n个顶点和m条边,每个边都有最大承载量,现在我要从1点运送货物到n点,求能运送货物的最大重量。
对于数据,第一行为t代表测试数据个数,第二行为n和m(意义见上),接着m行,每行三个整数分别是代表一条边的起点,终点及最大承重量。输出能运送货物的最大重量,格式见样例。注意数据输完后还要再多输一个空行。
对于数据,从1运到3有两种方案
方案1:1-2-3,其中1-2承重为3,2-3承重为5,则可以运送货物的最大重量是3(当大于3时明显1到不了2)
方案2:1-3,可知1-3承重为4,故此路可运送货物的最大重量是4,故答案输出4
因为此前也没做过图论题,对一些算法都不熟,再刚开始题意理解有问题,WA了几次,看懂题意后,搜了下别人的解题报告,说是Dijkstra的变形或这求最大生成树。也许对Dijkstra运用(压根就没用过)的不是很熟,一直不知道怎么下手,连样列都过不了。后就直接转到求最大生成树上去了,网上大部分代码是Prim算法,由于《算法入门竞赛经典》书没介绍该算法,暂时还没看,所以就选择Kruskal求最大生成树。然后选择Kruskal的一个问题就是连通分量的处理,《入门经典》是用的并查集来处理,因为对生成树算法不是很熟,就直接套的上面的模板。然后题目就是编程了求最大生成树,并找出从1-n的最小权值的边。当然,这棵树不用搜完,因为,你从1到n不一定会每一个节点都走过,当将1-n连通时此时的权值就是所求的值;转换用Kruskal时因为数组开大了MLE一次,开小了RE一次,最后决定还是动态分配靠谱些。不过因为一个小细节又WA了一次,最后改正,终于AC了,你说,AC一题我容易不!!!总之ACM搞图论的上辈子都是折翼的天使!!!
如果有时间,这题还会再做一遍的,用Prim算法和Dijkstra试一下!
参考代码:
1 //第一次提交的代码基本是套模板的,和自己写的出入较大,不习惯,将代码修改下感觉也许更好!,第一次提交的代码见最下面
2 #include<iostream>
3 #include<cstdlib>
4 #include<cstdio>
5 #include<cstring>
6 #include<algorithm>
7 #include<cmath>
8 using namespace std;
9 const int inf = (1<<20);
10 int p[1005]; //p是用于并查集的,r是用来存储边序号的
11 struct prog {
12 int w,v,u; //记录起点,终点,权值
13 };
14 bool cmp(prog a,prog b)
15 {//间接排序函数,
16 return a.w>b.w;
17 }
18 int find(int x)
19 {//并查集里的find函数,你懂的
20 return p[x]==x?x:p[x]=find(p[x]);
21 }
22 int main()
23 {
24 int t;
25 cin >> t;
26 int k = 1;
27 while(t--)
28 {
29 int n ,m;
30 cin >> n >> m;
31 prog *r;
32 r=new prog[m];
33 int i ;
34 for ( i = 0 ; i < m ; i ++ )
35 cin>>r[i].u>>r[i].v>>r[i].w; //输入边的信息
36
37 for ( i =1 ; i <= n; i ++ )
38 p[i]=i;//初始化并查集
39
40 sort(r,r+m,cmp);//根据边的权值的大小将边的序号进行排序,r[i]表示第i+1大的边存储在u,v,w数组中的序号
41 int ans=inf; //将答案初始化为最大值
42 for ( i = 0 ; i < m ; i ++ )
43 {
44 int x=find(r[i].u);
45 int y=find(r[i].v);
46 if(x!=y)
47 {//如果该边所在的两边不在同一个连通分量里,则连接该边
48 if(ans>r[i].w)//如果该边的权值比ans小(实际上一定不会比ans大),则更新ans
49 ans=r[i].w;
50 p[x]=y;//连接该边
51 if(find(1)==find(n))//当1和n连通时,则说明找到了一条从1到n的路,并且可知该路的所有边的权值都是最大的,故边的最小权值就是答案
52 break;
53 }
54 }
55 //输出答案,格式如题所述
56 cout<<"Scenario #"<<k<<":"<<endl;
57 cout<<ans<<endl<<endl;
58 k++;
59 }
60 return 0;
61 }
附:第一次参考代码
1 #include<iostream>
2 #include<cstdlib>
3 #include<cstdio>
4 #include<cstring>
5 #include<algorithm>
6 #include<cmath>
7 using namespace std;
8 const int inf = (1<<20);
9 int *p,*r; //p是用于并查集的,r是用来存储边序号的
10 int *u,*v,*w; //分别代表边的起点,终点,和权值,明显不是我的风格,先熟悉下模板,不得不这样写
11 bool cmp(const int a,const int b)
12 {//间接排序函数,
13 return w[a]>w[b];
14 }
15 int find(int x)
16 {//并查集里的find函数,你懂的
17 return p[x]==x?x:p[x]=find(p[x]);
18 }
19 int main()
20 {
21 int t;
22 cin >> t;
23 int k = 1;
24 while(t--)
25 {
26 int n ,m;
27 cin >> n >> m;
28 int i ;
29 u=new int[m];
30 v=new int[m];
31 w=new int[m];
32 r=new int[m];//动态分配
33 for ( i = 0 ; i < m ; i ++ )
34 {
35 int a , b , c ;
36 cin>>a>>b>>c;
37 u[i]=a;
38 v[i]=b;
39 w[i]=c;//加入边
40 }
41 p=new int[n+1];
42 for ( i =1 ; i <= n; i ++ )
43 p[i]=i;//初始化并查集
44 for ( i = 0 ;i < m ; i ++ )
45 r[i]=i;//初始化边序号
46 sort(r,r+m,cmp);//根据边的权值的大小将边的序号进行排序,r[i]表示第i+1大的边存储在u,v,w数组中的序号
47 int ans=inf; //将答案初始化为最大值
48 for ( i = 0 ; i < m ; i ++ )
49 {
50 int e=r[i];//找到第i+1大的边
51 int x=find(u[e]);
52 int y=find(v[e]);
53 if(x!=y)
54 {//如果该边所在的两边不在同一个连通分量里,则连接该边
55 if(ans>w[e])//如果该边的权值比ans小(实际上一定不会比ans大),则更新ans
56 ans=w[e];
57 p[x]=y;//连接该边
58 if(find(1)==find(n))//当1和n连通时,则说明找到了一条从1到n的路,并且可知该路的所有边的权值都是最大的,故边的最小权值就是答案
59 break;
60 }
61 }
62 //输出答案,格式如题所述
63 cout<<"Scenario #"<<k<<":"<<endl;
64 cout<<ans<<endl<<endl;
65 k++;
66 }
67 return 0;
68 }