次短路(两种方式) && 第K短路

次短路算法:

有两种比较简单实现次短路的思想

  • 方法一:用 dijkstra 算法 从起点开始 同时维护 【最短路数组(dis1[ ])】 和 【次短路 数组 (dis2[ ])】
  • 方法二:还是用到dijkstra 算法 分别用两个 dis1[ ] 数组 和  dis2[ ] 数组 分别 维护 从起点 和 从终点开始 的 最短路 ——然后枚举 所有边 , 将边的两个端点 连上 起点和终点 看是不是等于最短路,相等则跳过 不相等 则 更新 和 次短路(Inf)取min

题目:POJ - 3255

方式一:

tips:其中if(dis2[v] < w) continue; 这句 为true的时候,说明当前节点v 的次短路已经被更新过了 , 如果w比次短路大,说明它肯定是 >= 第三短路 , 也就不用更新了。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
using namespace std;
typedef long long LL;
const int Maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
int N , M;
int dis1[Maxn] , dis2[Maxn];
struct node{
	int v , w;
	friend bool operator < (node a , node b){
		return a.w > b.w;
	}
};
vector <node> G[Maxn];
void Dijkstra(){
	priority_queue <node> que;
	fill(dis1 , dis1+N+1 , Inf);
	fill(dis2 , dis2+N+1 , Inf);
	int start = 1;
	dis1[start] = 0;
	que.push((node){start , 0});
	node q;
	int v , w;
	while(!que.empty()){
		q = que.top();	que.pop();
		v = q.v , w = q.w;
		if(dis2[v] < w)	continue;
		int to_v , to_w;
		for(int i = 0 ; i < G[v].size() ; i++){
			to_v = G[v][i].v , to_w = G[v][i].w + w;
			if(dis1[to_v] > to_w){
				que.push((node){to_v , to_w});
				swap(dis1[to_v] , to_w);
			}
			if(dis2[to_v] > to_w && dis1[to_w] < to_w){
				dis2[to_v] = to_w;
				que.push((node){to_v , to_w});
			}
		}
	}

}

int main()
{
	while(~scanf(" %d %d",&N,&M)){
		for(int i = 1 ; i <= M ; i++){
			int u , v , w;	scanf(" %d %d %d",&u,&v,&w);
			G[u].push_back((node){v,w});
			G[v].push_back((node){u,w});
		}
		Dijkstra();
		printf("%d\n",dis2[N]);
	}
}

方式二:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
using namespace std;
typedef long long LL;
const int Maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
int N , M , cnt , ans;
int start = 1 , End = N;
int dis1[Maxn] , dis2[Maxn];
bool vis[Maxn];
struct node{
	int v , w;
	friend bool operator < (node a , node b){
		return a.w > b.w;
	}
};
struct edge{
	int x , y , w;
}A[Maxn << 1];
vector <node> G[Maxn];

void GetDis(int op){
	priority_queue <node> que;
	if(!op)	que.push((node){start , 0});
	else	que.push((node){End , 0});
	int v , w;
	node q;
	while(!que.empty()){
		q = que.top();	que.pop();
		v = q.v , w = q.w;
		if(vis[v])	continue;
		vis[v] = true;
		int to_v , to_w;
		for(int i = 0 ; i < G[v].size() ; i++){
			to_v = G[v][i].v , to_w = G[v][i].w + w;
			if(!op && dis1[to_v] > to_w){
				dis1[to_v] = to_w;
				que.push((node){to_v , to_w});
			} else if(op && dis2[to_v] > to_w){
				dis2[to_v] = to_w;
				que.push((node){to_v , to_w});
			}
		}
	}
}


void Dijkstra(){
	fill(dis1 , dis1+N+1 , Inf);
	fill(dis2 , dis2+N+1 , Inf);
	start = 1 , End = N;
	dis1[start] = dis2[End] = 0;
	fill(vis , vis+N+1 , false);
	GetDis(0);
	fill(vis , vis+N+1 , false);
	GetDis(1);
}
void FindCdl(){
	int flag = dis1[End];
	int x , y , w;
	ans = Inf;
	for(int i = 1 ; i <= cnt ; i++){
		x = A[i].x , y = A[i].y , w = A[i].w;
		int temp = dis1[x] + dis2[y] + w;
		if(temp == flag)	continue;
		else ans = min(ans , temp);
	}
}

int main()
{
	while(~scanf(" %d %d",&N,&M)){
		cnt = 0;
		for(int i = 1 ; i <= M ; i++){
			int u , v , w;	scanf(" %d %d %d",&u,&v,&w);
			G[u].push_back((node){v,w});
			G[v].push_back((node){u,w});
			A[++cnt].x = u , A[cnt].y = v , A[cnt].w = w;
			A[++cnt].x = v , A[cnt].y = u , A[cnt].w = w;
		}
		Dijkstra();
		FindCdl();
		printf("%d\n",ans);
	}
}

 

第K短路:

第K短路 其实 就是BFS + A*(A*==启发式搜索==优化剪枝)

首先求 从 终点 到 每个点的最短路 用 dis[ ] 数组存储

然后使用 A* 函数 , F[x] = H[x] + G[x]

题目:POJ 2449

具体讲解在代码内

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <stack>
#include <vector>
using namespace std;
typedef long long LL;
const int Maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
int N , M , K;
int start , End;
int ans;

//最短路部分
int dis[Maxn];
bool vis[Maxn];
struct node{
	int v , w;
	friend bool operator < (node a , node b){
		return a.w > b.w;
	}
};

/*
 * A* 启发式搜索函数 F[x] = H[x] + G[x]
 * 变量 Hx 表示搜索到当前点 所用的代价
 * 变量 Gx 是估价函数 (估价函数要小于等于实际值,否则出错)
 */
struct edge{
	int v , Hx , Gx;
	friend bool operator < (edge a , edge b){
		return a.Hx + a.Gx > b.Hx + b.Gx;
	}
};
/*
 * count 记录第几次BFS拓展到此点
 * 当 count == K 时  不再对此点继续进行拓展(因为拓展的点必定大于 第K短路)
 */
int Count[Maxn];

vector <node> G[Maxn] , G2[Maxn];

/*
 * (因为是有向图所以反向建图)
 * 求End到每个点的最短路
 */
void Dijkstra(){
	fill(vis , vis+N+1 , false);
	fill(dis , dis+N+1 , Inf);
	priority_queue <node> que;
	que.push((node){End , 0});
	dis[End] = 0;
	node q;
	int v , w;
	while(!que.empty()){
		q = que.top(); que.pop();
		v = q.v , w = q.w;
		if(vis[v])	continue;
		vis[v] = true;
		int to_v , to_w;
		for(int i = 0 ; i < G2[v].size() ; i++){
			to_v = G2[v][i].v , to_w = G2[v][i].w + w;
			if(dis[to_v] > to_w){
				dis[to_v] = to_w;
				que.push((node){to_v , to_w});
			}
		}
	}
}
/*
 * 第K短路算法 = A* + BFS
 */
void Astar(){
	ans = -1;
	fill(Count , Count+N+1 , 0);
	priority_queue <edge> que;
	que.push((edge){start , 0 , 0});
	edge q;
	int v , Hx , Gx;
	while(!que.empty()){
		q = que.top(); que.pop();
		v = q.v , Hx = q.Hx , Gx = q.Gx;
		Count[v]++;
		if(Count[v] == K && v == End){
			ans = Hx + Gx;
			break;
		}
		if(Count[v] > K)	continue;
		int to_v , to_hx , to_gx;
		for(int i = 0 ; i < G[v].size() ; i++){
			to_v = G[v][i].v;
			to_hx = Hx + G[v][i].w;
			to_gx = dis[to_v];
			que.push((edge){to_v , to_hx , to_gx});
		}
	}
	while(!que.empty())	que.pop();
	return;
}

int main()
{
	while(~scanf(" %d %d",&N,&M)){
		for(int i = 1 ; i <= N ; i++)	G[i].clear();
		for(int i = 1 ; i <= M ; i++){
			int u , v , w;	scanf(" %d %d %d",&u,&v,&w);
			G[u].push_back((node){v, w});
			G2[v].push_back((node){u, w});
		}
		scanf(" %d %d %d",&start , &End , &K);
		//此题要求start和End相同的时候 第一短路不是0 ,所以K++
		if(start == End)	K++;
		Dijkstra();
		Astar();
		printf("%d\n",ans);
	}
	return 0;
}

 

  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值