poj1797

11 篇文章 0 订阅
11 篇文章 0 订阅

题目不难,是最短路径的变形。大致题意为:

给定n个顶点,以及m条边的描述,每条边的描述包括:起点、终点、权重。现在要从顶点1出发到达顶点n,求路径中能够承受的最大权重。

首先题目的理解非常重要。本题要求出的是从顶点1到顶点n的所有可行路径中各边权值的最小值的最大值。即max(min(可行路径边))。很显然是最短路径的变形。这里给出两个思路。

首先贪心思想:模仿Dijkstra算法 O(n^2)

令dis[i]表示从顶点1到顶点i的可行路径中各边权值的最小值的最大值。

则我们可以模仿Dijkstra算法,先求出最大的dis值,然后依据已经求出的dis值来扩展其余点的dis值,最终1到其余所有点的dis值。那么应该如何扩展捏?可以从dis的定义出发,要求出所有可行路径中各权值的最小值的最大值,那么若能扩展,则必有min{dis[i],trim[i][j]}>dis[j],即可以找到一条边权值更大的可行路径。同时由于是求最大值,则初始化时要注意点之间的距离要为负无穷。

其次是dp思想:模仿floyd算法 O(n^3)

令dis[i][j]表示从顶点i到顶点j的各可行路径中各边权值的最小值的最大值。那么假设有顶点k序列,满足:i与k序列相连,k序列与j相连。则从i到j就有了一条可行路径:i——k序列——j。而这条路径的最短边为min(dis[i][k],dis[k][j]},于是dis[i][j]应该为所有这些边中的最大值。于是动态转移方程为:

dp[i][j]=Max{dis[i][j],min{dis[i][k],dis[k][j]}}

下面为代码:4132K+375MS

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 1010
#define Maxx(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
#define Inf 100000010
int trim[Max][Max]; //邻接矩阵
int dis[Max]; //从点1到其余各点的可行路径上边权值最小值的最大值
bool vis[Max];//标记是否已经求出dis
int Case,n,m; //情况数、点个数、边个数
void Dijkstra(){//dijkstra算法求从点1到其余各点的可行路径上边权值最小值的最大值
	int i,j;
	memset(vis,0,sizeof(vis)); //初始化为均未求出dis值
	dis[1]=0; //初始化1到1最大值为0
	vis[1]=true;//标记1已经求出dis值
	for(i=2;i<=n;i++) //初始化为边权值,注意若边实际不存在则其值为负无穷,在主函数中初始化
		dis[i]=trim[1][i];
	int maxint,ind;
	for(i=1;i<n;i++){ // 循环n-1次,求出每个顶点的dis值
		maxint=-Inf,ind=1; // 初始化为负无穷、并入顶点为1
		for(j=1;j<=n;j++)// 求出最大的dis值
			if(!vis[j] && dis[j]>maxint){
				maxint=dis[j];
				ind=j;
			}
		vis[ind]=true;  // 标记为已经求出,并入求出顶点集合
		for(j=1;j<=n;j++) //扩展各顶点
			if(!vis[j] && (Min(dis[ind],trim[ind][j]))>dis[j])//扩展条件
				dis[j]=Min(dis[ind],trim[ind][j]);
	}
	printf("%d\n\n",dis[n]); //输出最大值,注意有两个空行
}
	
int main(){
	int i,j,a,b,w;
	scanf("%d",&Case);
	for(int k=1;k<=Case;k++){ 
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)//初始化为负无穷
			for(j=1;j<=n;j++)
				trim[i][j]=-Inf;
		while(m--){ //输入边的信息
			scanf("%d%d%d",&a,&b,&w);
			trim[a][b]=trim[b][a]=w; //构造邻接矩阵
		}
        printf("Scenario #%d:\n",k); //输出
		Dijkstra();
	}
	return 0;
}
			

下面是dp TLE代码(仅供参考)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 1010
#define Maxx(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
#define Inf 100000010
int trim[Max][Max];
bool flag[Max][Max];
int dis[Max][Max];
int Case,n,m;
void floyd(){
	int i,j,k;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			if(i==j)
				dis[i][j]=0;
			else
				dis[i][j]=trim[i][j];
    for(k=1;k<=n;k++)
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
					dis[i][j]=Maxx(dis[i][j],Min(dis[i][k],dis[k][j]));
	printf("%d\n\n",dis[1][n]);
}
int main(){
	int i,j,a,b,w;
	scanf("%d",&Case);
	for(int k=1;k<=Case;k++){
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				trim[i][j]=-Inf;
		memset(flag,0,sizeof(flag));
		while(m--){
			scanf("%d%d%d",&a,&b,&w);
			trim[a][b]=trim[b][a]=w;
		}
        printf("Scenario #%d:\n",k);
	    floyd();
	}
	return 0;
}
		


 


 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值