链接:http://poj.org/problem?id=1797
题意:Hugo要把一些货物通过一个公路网络从点1运送到点n,每条公路有其自身的最大载重力wgt,公路是双向的。
求从点1到点n的所有公路中的最小载重量的最大值。即从点1到点n存在多条路径,对于每条路径又有一个最小的载重力。要求所有载重力中最大的那条路径,并输出。
这个题第一次做的时候没做出来,后来放了好久,今天又拿出来做,终于彻彻底底搞明白了。
最早就以为是用最小生成树的算法变形一下,生成最大树(必有n-1条边),然后再求这棵树中的最小的那条边。也就是说整个过程要生成n-1条边,才可以结束整个过程。
而这是错误的。
例如:
4 4
1 4 2
1 3 1
4 2 1
3 2 1
这个例子如果用上面的思想求,必然是1,而正确结果是2.
关键是题目要求只求从点1到n的一条路径即可,也就是说,即便还没达到n-1条边,只要到了这个点n,或者换句话说,点1和点n连通的时候,即可返回当前路径中最小的边。
清楚了这一点就好办了。
因为要求最小路径中的最大边,所以每一次选择边是选取最大的,这一点应该容易理解。所以,就可以用最短路或最小生成树的思想将其变形来做。
因为之前一直没搞清楚题意,写了prim提交,自己觉得明明正确啊!然后提交N次WA。。。哎。。然后就。。。
然后我就一口气写了三个版本的,其实可以算两个,一个是prim和kruskal,另一个是dijkstra,都只要变形一下就可以了。
/*Accept*/
/*Dijkstra*/
#include<iostream>
#include<cstring>
#define MAXN 1010
#define INF 1000005
#define max(a,b) a>b?a:b
using namespace std;
int n,m;
int map[MAXN][MAXN];
int dist[MAXN];
void dij()
{
int s[MAXN],mindis=INF;//数组s用于判断点是否被加入到路径中
//mindis用于存储当前路径中的最小边
int i,j;
for(i=1;i<=n;i++)
{
dist[i]=map[1][i]; //初始化dist[i]
s[i]=0; //最初没有点加入数组s
}
dist[1]=0;
s[1]=1; //起始点加入s
for(i=2;i<=n;i++)
{
int temp=0;
int u=1;
for(j=1;j<=n;j++)
if(!s[j]&&dist[j]>temp) //s[j]必须没有访问过,路径始终选择最大的,与求最短路相反
{
temp=dist[j];
u=j;
}
s[u]=1; //将点u加入数组s
if(temp<mindis) //更新mindis
mindis=temp;
if(u==n) //如果点1和点n连通时,则可直接输出mindis,然后跳出结束
{
cout<<mindis<<endl;
return;
}
for(j=1;j<=n;j++)
if(!s[j]&&map[u][j]>0)//s[j]未访问且u、j连通
{
int maxdis=max(dist[j],map[u][j]);//则取较大的那条边
dist[j]=maxdis;
}
}
}
int main()
{
int t,i,j,k,maxn;
int a,b,c;
cin>>t;
for(k=1;k<=t;k++)
{
cin>>n>>m;
if(n==1) //n=1的情况是我自己加的,其实不加也能过
//但加了输出的结果更符合实际些,个人觉得
{
maxn=0;
for(i=0;i<m;i++)
{
cin>>a>>b>>c;
if(maxn<c)
maxn=c;
}
cout<<"Scenario #"<<k<<":"<<endl;
cout<<maxn<<endl;
cout<<endl;
continue;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
map[i][j]=-1;//因为每次选取最大边,所以初值赋为1
for(i=0;i<m;i++)
{
cin>>a>>b>>c;
map[a][b]=map[b][a]=c;
}
cout<<"Scenario #"<<k<<":"<<endl;
dij();
cout<<endl;
}
return 0;
}
/*Accept*/
/*Prim*/
#include<iostream>
#include<cstring>
#define MAX 1005
#define INF 1000005
using namespace std;
int trans[MAX][MAX];
int lowcost[MAX],closest[MAX];
int n,m;
int maxPrim(int v)
{
int i,j,maxdis,mindis,minone;
for(i=1;i<=n;i++)
{
lowcost[i]=trans[v][i];
closest[i]=v;
}
lowcost[v]=INF;
mindis=INF;
for(i=0;i<n-1;i++)
{
maxdis=0;
for(j=1;j<=n;j++)
if(lowcost[j]>0&&maxdis<lowcost[j]&&lowcost[j]!=INF)//与最小树相反,每次取最大
//lowcost[j]=-1,说明点j和点v(起始点)是不通的
//lowcost[j]=INF,说明点j已经在生成树中了
{
maxdis=lowcost[j];
minone=j;
}
if(mindis>maxdis)
{
mindis=maxdis;
}
if(minone==n) //当点1和点n连通时,即可输出mindis,并结束程序
return mindis;
lowcost[minone]=INF;
for(j=1;j<=n;j++)
if(trans[j][minone]>lowcost[j]) //和上面一样也是取最大
{
lowcost[j]=trans[j][minone];
closest[j]=minone;
}
}
return mindis;
}
int main()
{
int t,i,j,k,maxn;
int a,b,c;
cin>>t;
for(k=1;k<=t;k++)
{
cin>>n>>m;
if(n==1)
{
maxn=0;
for(i=0;i<m;i++)
{
cin>>a>>b>>c;
if(maxn<c)
maxn=c;
}
cout<<"Scenario #"<<k<<":"<<endl;
cout<<maxn<<endl;
cout<<endl;
continue;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
trans[i][j]=-1;//赋初值为-1,因为是求最大生成树
for(i=0;i<m;i++)
{
cin>>a>>b>>c;
trans[a][b]=c;
trans[b][a]=c;
}
maxn=maxPrim(1);
cout<<"Scenario #"<<k<<":"<<endl;
cout<<maxn<<endl;
cout<<endl;
}
return 0;
}
/* Accept */
/*Kruskal*/
#include<iostream>
#include<cstring>
#include<algorithm>
#define MAX 100005
#define MAXN 1010
#define INF 1000005
using namespace std;
int n,m;
struct Edge //边的结构体
{
int sta,end,wgt;
}edge[MAX];
int cmp(Edge a,Edge b)
{
return a.wgt>b.wgt;
}
int seeks(int *set,int v)//并查集应用
{
int i;
i=v;
while(set[i]>0)
i=set[i];
return i;
}
void kruskal()
{
int set[MAXN],v1,v2,i,j;
int mindis=INF;
for(i=1;i<=n;i++) //set[i]初始化为0,但我一般见到最多的貌似是set[i]=i;
//如果是set[i]=i,那么,上面查找的函数应该是这样的了
//while(set[i]!=i)i=set[i];
//当然这样也是可以的,我一直套用的是这个模板。
set[i]=0;
i=1;
j=0;
while(i<=n-1&&j<m) //i指当前生成树的边数,生成树要找n-1条边,j是边数
{
v1=seeks(set,edge[j].sta);
v2=seeks(set,edge[j].end);
if(v1!=v2) //v1和v2不在同一个集合中
{
set[v1]=v2;
if(mindis>edge[j].wgt) //mindis的含义就不在赘述了
mindis=edge[j].wgt;
if(seeks(set,1)==seeks(set,n)) //如果相等,则说明点1和点n已经连通
//一开始我把这个判断条件写错了,WA了N次。
{
cout<<mindis<<endl;
return ;
}
i++; //如果满足条件,则生成树的边要加1
}
j++; //j每次循环都要加1
}
return ;
}
int main()
{
int t,i,k,maxn;
int a,b,c;
cin>>t;
for(k=1;k<=t;k++)
{
cin>>n>>m;
if(n==1)
{
maxn=0;
for(i=0;i<m;i++)
{
cin>>a>>b>>c;
if(maxn<c)
maxn=c;
}
cout<<"Scenario #"<<k<<":"<<endl;
cout<<maxn<<endl;
cout<<endl;
continue;
}
for(i=0;i<m;i++)
{
cin>>edge[i].sta>>edge[i].end;
cin>>edge[i].wgt;
}
sort(edge,edge+m,cmp);
cout<<"Scenario #"<<k<<":"<<endl;
kruskal();
cout<<endl;
}
return 0;
}