poj1797 Heavy Transportation

来源:互联网 发布:netbeans运行java项目 编辑:程序博客网 时间:2024/05/22 06:27

链接: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:busing 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;            //起始点加入sfor(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 1000005using 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 1000005using 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;}


 

0 0
原创粉丝点击