最短路

一.Dijkstra

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <queue>
using namespace std ;

const int MAXN = 105 ;
const int INF = 0xfffff ;

struct node{
	int p ;
	int value ;
	friend bool operator < (node a,node b){  //重载;
		return a.value > b.value ;
	}
};
priority_queue<node> Q ;  //优先队列找出最短的那条边;

struct edge{    //静态邻接表之edge
	int to ;
	int fee ;
	int next ;
}edge[MAXN * MAXN] ;  
int top ;
int hand[MAXN] ;  //静态邻接表之hand

int map[MAXN][MAXN] ;

int dis[MAXN] ;
int vist[MAXN] ;
int path[MAXN] ;   //用于储存最小路径的路途;

void addedge(int f,int t,int v){   //静态邻接表之操作;
	edge[top].to = t ;
	edge[top].fee = v ;
	edge[top].next = hand[f] ;
	hand[f] = top++ ;
}

void init(int n,int m){  //初始化
	int i , j ;
	memset(hand,-1,sizeof(hand)) ;
	memset(path,-1,sizeof(path)) ;
	memset(vist,0,sizeof(vist)) ; 
	top = 0 ;
	while(m--){
		int a , b , c ;
		scanf("%d%d%d",&a,&b,&c) ;
		addedge(a,b,c) ;
		addedge(b,a,c) ;
	}
	for(i = 0 ; i <= n ; i++){
		dis[i] = INF ;
	}
	dis[1] = 0 ;
}

void dijkstra(int n){
	while(!Q.empty())  Q.pop() ;  //清空队列;
	node now ;
	now.p = 1 ;  //起始点;
	now.value = 0 ;
	Q.push(now) ;
	while(!Q.empty()){
		now = Q.top() ;
		Q.pop() ;
		int u = now.p ;
		if(vist[u])  continue ;
		vist[u] = 1 ;
		int i ;
		for(i = hand[u] ; i != -1 ; i = edge[i].next){
			int t = edge[i].to ;
			if(vist[t])  continue ;
			int v = edge[i].fee ;
			if(dis[u] + v < dis[t]){
				dis[t] = dis[u] + v ;
				node next ;
				next.p = t ;
				next.value = dis[t] ;
				Q.push(next) ; 
				path[t]=u;
			}
		}
	}
}

int main(){
	int n , m ;
	int i ;
	int ans[MAXN] ;
	while(scanf("%d%d",&n,&m) != EOF){
		if(n == 0 && m == 0)  break ;
		init(n,m) ;
		dijkstra(n) ;
		printf("%d\n",dis[n]) ;
		int t = 0 ;
		for(i = n ; i != -1 ; i = path[i]){
			ans[t++] = i ;
		}
		i=t-1;
		printf("%d",ans[i--]);
		for(; i >= 0 ; i--){
			printf(" %d",ans[i]) ;
		}
		printf("\n") ;
	}
	return 0 ;
}
和最小生成树的prim()如出一辙。

关键在于能一个点一个点和一条边一条边的确定下来。

例题

        HDU 1535  在一个有向图中,一个点到其他n-1个点来回的最短路径之和;也可以用flody,但是后者要求n在300左右。



二.spfa

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <queue>
using namespace std ;
const int MAXN = 105 ;
const int INF = 0xfffff ;
queue<int> q ;
struct edge{ //静态邻接表之edge
	int to ;
	int fee ;
	int next ;
}edge[MAXN * MAXN] ;
int top ;
int hand[MAXN] ; //静态邻接表之hand
int cout[MAXN];
int map[MAXN][MAXN] ;
int dis[MAXN] ;
int path[MAXN] ; //用于储存最小路径的路途;
int flag=0;
void addedge(int f,int t,int v){ //静态邻接表之操作;
	edge[top].to = t ;
	edge[top].fee = v ;
	edge[top].next = hand[f] ;
	hand[f] = top++ ;
}
void init(int n,int m){ //初始化
	int i ;
	memset(hand,-1,sizeof(hand)) ;
	memset(path,-1,sizeof(path)) ;
	memset(cout,0,sizeof(cout));
	top = 0 ;
	while(m--){
		int a , b , c ;
		scanf("%d%d%d",&a,&b,&c) ;
		addedge(a,b,c) ;
		addedge(b,a,c) ;
	}
	for(i = 0 ; i <= n ; i++){
		dis[i] = INF ;
	}
	dis[1] = 0 ;
}
void spfa(int n)
{
	int inqueue[MAXN] ;
	memset(inqueue,0,sizeof(inqueue)) ;
	int now = 1 ;
	q.push(1) ;
	cout[1]++;
	inqueue[1] = 1 ;
	while(!q.empty())
	{
		now = q.front() ;
		q.pop() ;
		inqueue[now] = 0 ;
		int i ;
		for(i = hand[now] ; i != -1 ; i = edge[i].next)
		{
			int t = edge[i].to ;
			int value = edge[i].fee ;
			if(dis[now] + value < dis[t])
			{
				dis[t] = dis[now] + value ;
				path[t] = now ;
				if(!inqueue[t]) //一旦dis[t]更新了就要压入队列;
				{
					q.push(t) ;
					cout[t]++; //累计进队列的t的次数;
					inqueue[t] = 1 ;
					if(cout[t]>n) flag=1; //表示有负环;
				}
			}
		}
	}
}
int main(){
	int n , m ;
	int i ;
	int ans[MAXN] ;
	while(scanf("%d%d",&n,&m) != EOF){
		if(n == 0 && m == 0) break ;
		init(n,m) ;
		spfa(n) ;
		printf("%d\n",dis[n]) ;
		int t = 0 ;
		for(i = n ; i != -1 ; i = path[i]){
			ans[t++] = i ;
		}
		i=t-1;
		printf("%d",ans[i--]);
		for(; i >= 0 ; i--){
			printf(" %d",ans[i]) ;
		}
		printf("\n") ;
	}
	return 0 ;
}
例题        POJ 1613 权值未时间,而通过每条路又有时间规定,不能把最小边确定下来,所以不能用Dijkstra。还有输入要注意,不然会TLE;        
 

               HDU 2489 负环问题,只能用spfa,也是因为不能把最小边确定下来;判断是否出现负环,用一个cout数组累计进入队列的i,cont[ i ]大于n就是有负环;

三.Flody

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <queue>
using namespace std ;

const int MAXN = 105 ;
const int INF = 0xfffff ;

int map[MAXN][MAXN] ;

void init(int n,int m){
	int i , j ;
	for(i = 1 ; i <= n ; i++){
		for(j = 1 ; j <= n ; j++){
			map[i][j] = INF ;
		}
	}
	while(m--){
		int a , b , c ;
		scanf("%d%d%d",&a,&b,&c) ;
		if(map[a][b] < c)  continue ;
		map[a][b] = map[b][a] = c ;
	}
}

void floyd(int n){
	int i , j , k ;
	for(k = 1 ; k <= n ; k++){
		for(i = 1 ; i <= n ; i++){
			for(j = 1 ; j <= n ; j++){
				if(i != j && j != k && i != k && map[i][j] > (map[i][k] + map[k][j]))
					map[i][j] = map[i][k] + map[k][j] ;
			}
		}
	}
}

int main(){
	int n , m ;
	int i ;
	int ans[MAXN] ;
	while(scanf("%d%d",&n,&m) != EOF){
		if(n == 0 && m == 0)  break ;
		init(n,m) ;
		floyd(n) ;
		printf("%d\n",map[1][n]) ;
	}
	return 0 ;
}

例题

       HDU 1217 map映射+flody;




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值