poj1797 Heavy Transportation

链接: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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值