【模板】k 短路

文章讲述了两种方法计算从s到t的k次最短路径,一种是先用Dijkstra算法求最短路径,然后用A*算法进行优化;另一种是利用最短路树和可持久化堆来实现,减少了计算复杂性。
摘要由CSDN通过智能技术生成

k短路是问你从 s s s t t t 的第 k k k 短路。

第一种做法是用 A ∗ A^{*} A 算法来做,先跑一遍 Dijkstra,接着用 A ∗ A^* A 跑一边来优化。有个性质就是这个点第 k k k 次从队列中出来那么这个就是它的第 k k k 短路了。特判如果 s = = t s==t s==t 的话, k k k 要加一。
具体看代码

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N = 1005;
struct node{
	int v,w;
};
int n,m,k,a,b;
int dist[N];
vector<node>road[N],rroad[N];//road正rroad反
bool dij()//先跑一遍最短路
{
	priority_queue<pii,vector<pii>,greater<pii> >q;
	memset(dist,0x3f,sizeof(dist));
	dist[b] = 0;
	q.push((pii){0,b});
	while(!q.empty())
	{
		int u = q.top().second;
		int len = q.top().first;
		q.pop();
		if(len != dist[u])
		{
			continue;
		}
		for(int i = 0;i < rroad[u].size();i++)
		{
			int v = rroad[u][i].v;
			int l = rroad[u][i].w;
			if(dist[v] > dist[u] + l)
			{
				dist[v] = dist[u] + l;
				q.push((pii){dist[v],v});
			}
		}
	}
	return dist[0] != dist[a];
}

int f[N];
void write(int x)
{
	cout << x << endl;
	if(x != a)
	{
		write(f[x]);
	}
	printf("%d",x);
	if(x != b)
	{
		printf("-");
	}
}
struct point{
	int first,second;
	vector<int>t;
	bool operator<(const point &x)const{
		return first + dist[second] > x.first + dist[x.second];
	}
};
void astar()//在跑一遍最短路
{
	priority_queue<point>q;
	vector<int>t;
	t.push_back(0);
	q.push((point){0,a,t});
	while(!q.empty())
	{
		int u = q.top().second;                       
		int len = q.top().first;
		t = q.top().t;
		q.pop();
		if(u == b)
		{
			k--;
			cout << len << endl;
			if(k == 0)
			{
				return;
			}
		}
		for(int i = 0;i < road[u].size();i++)
		{
			int v = road[u][i].v;
			int l = road[u][i].w;
			vector<int>tt = t;
			tt.push_back(v);
			q.push((point){l + len,v,tt});
		}
	}
	while(k--)
	{
		cout << -1 << endl;
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	a = n;
	b = 1;
	while(m--)
	{
		int u,v,l;
		scanf("%d%d%d",&u,&v,&l);
		road[u].push_back((node){v,l});
		rroad[v].push_back((node){u,l});
	}
	if(!dij())
	{
		printf("NO\n");
		return 0;
	}
	if(a == b)
	{
		k++;
	}
	astar();
	return 0;
}

模板题:P2901 [USACO08MAR] Cow Jogging G

第二种做法是使用最短路和可持久化合并堆来优化。

我们设是求从 s s s t t t k k k 短路。我们先建一个反图,接着跑一遍从 t t t 开始的最短路,建立以点 t t t 为根的最短路树,对于每一条非树边(它是从点 u u u 到点 v v v,权值为 w w w),我们可以得出它的新的贡献为 d i s t v + w − d i s t u dist_v + w - dist_u distv+wdistu,把这条边的新的权值推到根为 r o o t u root_u rootu 的可持久化堆中,接着我们建立一个小根堆。一开始我们在小根堆中推入 t r r o o t u tr_{root_u} trrootu,每次替换都有两种手段。

  1. 更换成同样是以 u u u 开始的非树边,注意是要严格大于当前边的新的贡献值
  2. 推入 t r r o o t v tr_{root_v} trrootv 来代替

同样的第 k k k 个出队的就是 s s s t t t k k k 短路。
具体看代码

#include <bits/stdc++.h>
using namespace std;
#define pii pair<double,int>
const double ep = 1e-8;
const int N = 10005;
const int M = 200005;
int n,m;
double E;
struct node{
	int id,v;
	double w;
};
vector<node>road[N],rroad[N];
double dist[N];
int root[N];
namespace LT{
	struct point{
		int lson,rson,last;
		int dist;
		double val;
	}tr[M * 20];
	int cnt = 0;
	int create(double v,int last)
	{
		int u = ++cnt;
		tr[u].val = v;
		tr[u].last = last;
		tr[u].dist = tr[u].lson = tr[u].rson = 0;
		return cnt;
	}
	int cop(int id)
	{
		int u = ++cnt;
		tr[u] = tr[id];
		return u;
	}
	int merge(int x,int y)
	{
		if(x == 0 || y == 0)
		{
			return x | y;
		}
		if(tr[x].val > tr[y].val)
		{
			swap(x,y);
		}
		int u = cop(x);
		tr[u].rson = merge(tr[u].rson,y);
		if(tr[tr[u].lson].dist < tr[tr[u].rson].dist)
		{
			swap(tr[u].lson,tr[u].rson);
		}
		tr[u].dist = tr[tr[u].rson].dist + 1;
		return u;
	}
	void insert(int &rt,double val,int last)
	{
		rt = merge(create(val,last),rt);
	}
}
using namespace LT;
priority_queue<pii,vector<pii>,greater<pii> >q;
void dij()
{
	for(int i = 0;i < N;i++)
	{
		dist[i] = 1e9;
	}
	dist[n] = 0;
	q.push((pii){0.0,n});
	while(!q.empty())
	{
		int u = q.top().second;
		double len = q.top().first;
		q.pop();
		if(dist[u] != len)
		{
			continue;
		}
		for(int i = 0;i < rroad[u].size();i++)
		{
			int v = rroad[u][i].v;
			double l = rroad[u][i].w;
			if(dist[v] > dist[u] + l)
			{
				dist[v] = dist[u]+ l;
				q.push((pii){dist[v],v});
			}
		}
	}
}
int f[N];
int st[N];
bool vis[N];
int top;
bool line[M];
void dfs(int u)
{
	st[++top] = u;
	vis[u] = true;
	for(int i = 0;i < rroad[u].size();i++)
	{
		int v = rroad[u][i].v;
		double l = rroad[u][i].w;
		if(dist[v] == dist[u] + l && vis[v] == false)
		{
			line[rroad[u][i].id] = true;
			f[v] = u;
			dfs(v);
		}
	}
}
void build()
{
	for(int i = 1;i<=top;i++)
	{
		int u = st[i];
		root[u] = root[f[u]];
		for(int i = 0;i < road[u].size();i++)
		{
			int id = road[u][i].id;
			int v = road[u][i].v;
			double w = road[u][i].w;
			if(!line[id] && dist[v] != dist[0])
			{
				insert(root[u],dist[v] + w - dist[u],v);
			}
		}
	}
}
int kshort()
{
	int cnt = 1;
	E-=dist[1];
	priority_queue<pii,vector<pii>,greater<pii> >q;
	q.push((pii){dist[1] + tr[root[1]].val,root[1]});
	while(!q.empty())
	{
		double len = q.top().first;
		int u = q.top().second;
		q.pop();
		if(len -ep>E)break;
		E-=len - ep;
		cnt++;
		int l = tr[u].lson;
		int r = tr[u].rson;
		int top = tr[u].last;
		if(root[top])
		{
			q.push((pii){len + tr[root[top]].val,root[top]});
		}
		if(l)
		{
			q.push((pii){len + tr[l].val - tr[u].val,l});
		}
		if(r)
		{
			q.push((pii){len + tr[r].val - tr[u].val,r});
		}
	}
	return cnt;
}
int main()
{
	scanf("%d%d%lf",&n,&m,&E);
	for(int i = 1;i <= m;i++)
	{
		int u,v;
		double w;
		scanf("%d%d%lf",&u,&v,&w);
		if(u == n)
		{
			i--;
			m--;
			continue;
		}
		road[u].push_back((node){i,v,w});
		rroad[v].push_back((node){i,u,w});
	}
	dij();
	dfs(n);
	build();
	printf("%d\n",kshort());
	return 0;
}

模板题:P2483 【模板】k 短路 / [SDOI2010] 魔法猪学院

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值