K短路 (A*算法) [Usaco2008 Mar]牛跑步&[Sdoi2010]魔法猪学院

Posted Winniechen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K短路 (A*算法) [Usaco2008 Mar]牛跑步&[Sdoi2010]魔法猪学院相关的知识,希望对你有一定的参考价值。

A*属于搜索的一种,启发式搜索,即:每次搜索时加一个估价函数

这个算法可以用来解决K短路问题,常用的估价函数是:已经走过的距离+期望上最短的距离

通常和Dijkstra一起解决K短路

BZOJ1598:牛跑步

求前K短路

因为A*算法我们每次用来向外拓展的是估价函数最小的点,那么,我们必定能够得到,第一个到达n的是最短路。(Dijkstra的贪心,可证)

那么,我们思考一下,第二个到达n的就是次短路!

由此观之:第K个到达的就是K短路

因此,A*算法可以用来解决K短路问题

附上代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <map>
using namespace std;
#define N 10005
#define ll long long
struct node
{
	int to,next,val;
}e[N*10],E[N*10];
int head[N],cnt,cnt1,n,m,K,vis[N],head1[N];
int dis[N];
void add(int x,int y,int z)
{
	e[cnt].to=y;
	e[cnt].next=head[x];
	e[cnt].val=z;
	head[x]=cnt++;
}
void add1(int x,int y,int z)
{
	E[cnt1].to=y;
	E[cnt1].next=head1[x];
	E[cnt1].val=z;
	head1[x]=cnt1++;
}
priority_queue<pair<int ,int > >q;
void Dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	dis[n]=0;
	q.push(make_pair(0,n));
	while(!q.empty())
	{
		int x=q.top().second;q.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(int i=head1[x];i!=-1;i=E[i].next)
		{
			int to1=E[i].to;
			if(dis[to1]>dis[x]+E[i].val)
			{
				dis[to1]=dis[x]+E[i].val;
				q.push(make_pair(-dis[to1],to1));
			}
		}
	}
}
void Astar(int s)
{
	q.push(make_pair(-dis[s],s));
	while(!q.empty())
	{
		int d=q.top().first;int x=q.top().second;q.pop();
		if(x==n)
		{
			K--;
			printf("%d\n",-d);
			if(!K)return ;
		}
		for(int i=head[x];i!=-1;i=e[i].next)
		{
			int to1=e[i].to;
			q.push(make_pair(d+dis[x]-e[i].val-dis[to1],to1));
		}
	}
}
int main()
{
	memset(head1,-1,sizeof(head1));
	memset(head,-1,sizeof(head));
	scanf("%d%d%d",&n,&m,&K);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(y,x,z);
		add1(x,y,z);
	}
	Dijkstra();
	Astar(1);
	while(K--)puts("-1");
	return 0;
}

1975: [Sdoi2010]魔法猪学院

这个贪心贪的很显然,为了尽可能的多,自然是选择前K短路

那么,同样,我们用Dijkstra的贪心方法求n的前K短路

但是这种方法实际的时间复杂度不是很对,理论上可以过很多题。这道题BZOJ可以过,但是洛谷上过不去

附上不完美的代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <map>
using namespace std;
#define N 5005
#define ll long long
struct node
{
	int to,next;
	double val;
}e[200005],E[200005];
int head[N],cnt,cnt1,n,m,vis[N],head1[N],ans;
double dis[N],K;
void add(int x,int y,double z)
{
	e[cnt].to=y;
	e[cnt].next=head[x];
	e[cnt].val=z;
	head[x]=cnt++;
}
void add1(int x,int y,double z)
{
	E[cnt1].to=y;
	E[cnt1].next=head1[x];
	E[cnt1].val=z;
	head1[x]=cnt1++;
}
priority_queue<pair<double ,int > >q;
void Dijkstra()
{
	for(int i=0;i<N;i++)dis[i]=1e9;
	dis[n]=0;
	q.push(make_pair(0,n));
	while(!q.empty())
	{
		int x=q.top().second;q.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(int i=head1[x];i!=-1;i=E[i].next)
		{
			int to1=E[i].to;
			if(dis[to1]>dis[x]+E[i].val)
			{
				dis[to1]=dis[x]+E[i].val;
				q.push(make_pair(-dis[to1],to1));
			}
		}
	}
}
void Astar(int s)
{
	q.push(make_pair(-dis[s],s));
	while(!q.empty())
	{
		double d=q.top().first;int x=q.top().second;q.pop();
		if(x==n)
		{
			if(K+d<0-1e-9)return ;
			K+=d;
			ans++;
		}
		for(int i=head[x];i!=-1;i=e[i].next)
		{
			int to1=e[i].to;
			q.push(make_pair(d+dis[x]-e[i].val-dis[to1],to1));
		}
	}
}
int main()
{
	memset(head1,-1,sizeof(head1));
	memset(head,-1,sizeof(head));
	scanf("%d%d%lf",&n,&m,&K);
	for(int i=1;i<=m;i++)
	{
		int x,y;double z;
		scanf("%d%d%lf",&x,&y,&z);
		add(x,y,z);
		add1(y,x,z);
	}
	Dijkstra();
	Astar(1);
	printf("%d\n",ans);
	return 0;
}

正解是用可持久化可并堆+A*算法,挖坑待填

以上是关于K短路 (A*算法) [Usaco2008 Mar]牛跑步&[Sdoi2010]魔法猪学院的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1598: [Usaco2008 Mar]牛跑步

bzoj1598: [Usaco2008 Mar]牛跑步

cogs 133. [USACO Mar08] 牛跑步 A*算法

bzoj1598: [Usaco2008 Mar]牛跑步

[USACO Mar08] 牛跑步 --k短路

[Luogu2901][USACO08MAR]牛慢跑Cow Jogging Astar K短路