POJ - 2387 Til the Cows Come Home

POJ - 2387

题目链接:
http://poj.org/problem?id=2387
题目大意:
给出了m和n,n代表的是点的个数,m代表的是边的个数,然后接下来的m行是两个点以及这两个点之间的距离,当然列出的两个点之间是相互可达的,问如果想从点1到达点n那么至少需要行走多远的距离才可以到达。
应用方法:Bellmax-Ford
这个方法适用稀疏图和边关系密切的情况下,可以解决负权边问题,时间复杂度是
O(NM),如果用迪杰斯特拉就无法解决带有负权边的问题但是解决这个问题还是可以的,但是就要时刻注意有重边的问题,有重边时要选择路径短的那一条存入数组中
1.
迪杰斯特拉:单源最短路
这里是用到了贪心的思想来松弛这个图,每一次都要找到第一个点到其他点的的路径最短的那一个,用过的点要用数组进行一下标记否则无法继续进行操作,外部循环n次,即把所有点往外能延伸的点都进行了松弛操作,那么一定可以找到最短路,这里就要注意迪杰斯特拉这种方法是不可以解决含有负权边的问题的

迪杰斯特拉:
适合解决稠密图和顶点关系密切的情况,不可以处理含有负权边的情况,也不可以判断是否有负权回路,是贪心的思想

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 99999999
int h[1005][1005];
int book[1005];
int dis[1005];
int main(){
    int m,n;
    int p,q,r;
    while(scanf("%d %d",&m,&n)!=EOF){
    	for(int i=1;i<=n;i++)
    	    book[i]=0;
    	for(int i=1;i<=n;i++){
		    for(int j=1;j<=n;j++){
                if(i==j)
				    h[i][j]=0;    //在这里把所有的h[i][j]赋值成inf也是可以的				
				else			
 			        h[i][j]=inf;
		    }
        }
	    for(int i=1;i<=m;i++){
    		cin>>p>>q>>r;
    		if(h[p][q]>r){         //这里要注意可能存在重边的问题,即两个点之间可能存在长度不同的通路
			    h[p][q]=r;
    		    h[q][p]=r;
    		}
		}
		for(int i=1;i<=n;i++)
		    dis[i]=h[1][i];        //初始化dis数组
		for(int i=1;i<=n;i++){     //外层进行了n次循环,对这个图进行了松弛操作
			int min=inf;
			int u;
			for(int j=1;j<=n;j++){
				if(dis[j]<min&&book[j]==0){
				    min=dis[j];
					u=j;
				}
			}
			book[u]=1;
			for(int k=1;k<=n;k++){
				if(dis[k]>h[u][k]+dis[u])
				   dis[k]=h[u][k]+dis[u];
			}
		}
		cout<<dis[n]<<endl;
	}
	return 0;
}

2.第二种方法是Bellman-Ford

适用于稀疏图和边关系密切,可以解决负权,可以判定是否有负权回路

外部循环n-1次,内部循环m次,即枚举每一条边。数组dp的作用是记录源点到其余各个顶点的最短路径。在进行第一次的松弛之后,得到的是从1号顶点“只能经过一条边”到达其余各个顶点的最短路径长度。在第2轮在对所有的边进行松弛之后,得到的是从1号顶点“最多经过两条边”到达其余各个顶点的最短路径,这也就是为什么外部循环要进行n-1次的原因,要历经每一个可能进行松弛的边
代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
#define inf 1<<29
typedef struct{
	int a,b,c;
}hh;
hh h[2005];
int dp[1005];
int main(){
	int n,m;
	while(scanf("%d %d",&m,&n)!=EOF){
	    for(int i=1;i<=m;i++)
			scanf("%d %d %d",&h[i].a,&h[i].b,&h[i].c);
    	for(int i=2;i<=n;i++) 
	        dp[i]=inf;
	    dp[1]=0;
        for(int i=1;i<=n;i++){
		    for(int j=1;j<=m;j++){
			    if(dp[h[j].a]>h[j].c+dp[h[j].b]) dp[h[j].a]=h[j].c+dp[h[j].b];   //此题是无向图,点与点之间相互可达,所以要进行两次判断
			    if(dp[h[j].b]>h[j].c+dp[h[j].a]) dp[h[j].b]=h[j].c+dp[h[j].a];
		    }
	    }
		printf("%d\n",dp[n]);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

协奏曲❤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值