K短路问题还是很普遍的,了解一下K短路很有必要,顺便学会A*的简单应用更好。
A*算法,是一种启发式搜索算法,我们可以自己设定一个估价函数,这样我们的搜索过程就会优先选择有更好的估价函数的点进行搜索。
在最短路中,A*的估价函数定义如下:f(p)=g(p)+h(p),g(p)表示当前从起始点s到点p的距离,而h(p)就是从终点到p的最短路距离,(暂且是最短路,因为k短路必须由最短路出发向前找的,每次都是除了上一次的最短路),则f(p)就意味着从起点s到当前路径p之后再走到终点t的最短路,这样,我们的每次扩展都是有方向的扩展,从而降低求解速度和降低搜索的状态数,并且保证不优于最优解的同时尽量靠近最优解。
思路到了,做法也必须跟上。我们用最短路来估价,这就要求对h(p)函数进行一个预处理,所以我们可以求出终点到各个点的最短路,作为h(p)值,再从s开始遍历k次,每次都以上一次为最短路,直到我们第k次到达终点,此时的路径长度就是k短路。
详细部分看下面几个模板题的代码。
(1)POJ2449
题目链接:https://vjudge.net/problem/POJ-2449
题目大意:给出一个图,然后给出一个起点个一个终点,求这两点间的第K短路。
本题中是可以走重复的路的,所以如果一张图中有一个环的话,无论求第几短路都是存在的。
也就是,样例询问的第二短路,就是先从1到2,再从2到1,再从1到2,结果为5+4+5=14
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
/*
POJ 2449 A*求k短路模板题
*/
const int maxn=1005;
const int maxm=500005;
const int INF=0x3f3f3f3f;
struct Edge
{
int u;
int v;
int next;
int w;
}edges[maxm],re_edges[maxm];//re_edges为反向边
struct ANode
{
int f,g,v;//分别对应f函数,g函数和v编号
bool operator <(const ANode &rhs)const
{
if(rhs.f==f)return rhs.grhs.f;//f小的优先被考虑
}
ANode(int _f,int _g,int _v):f(_f),g(_g),v(_v){}
};
int head[maxn],re_head[maxn],tot;
void init()
{
memset(head,-1,sizeof(head));
memset(re_head,-1,sizeof(re_head));
tot=0;
}
int dis[maxn];
int vis[maxn];
int n,m,S,T,k;
void add_edges(int u,int v,int cost)
{
edges[tot].u=u;
edges[tot].v=v;
edges[tot].w=cost;
edges[tot].next=head[u];
head[u]=tot;
//反向边,因为要计算点p到最终点的最短路,就直接计算最终点到p的最短路
re_edges[tot].u=v;
re_edges[tot].v=u;
re_edges[tot].w=cost;
re_edges[tot].next=re_head[u];
re_head[u]=tot++;
}
//处理一个距离数组,表示从终点开始到各个点的最短路
int cnt[maxn];//每个点的入队次数
bool SPFA(int s)
{
for(int i=1;i<=n;i++)dis[i]=INF;
memset(vis,0,sizeof(vis));
queueQ;
while(!Q.empty())Q.pop();
vis[s]=1;
dis[s]=0;
Q.push(s);
memset(cnt,0,sizeof(cnt));
cnt[s]=1;
while(!Q.empty())
{
int u=Q.front();Q.pop();
vis[u]=0;
for(int i=re_head[u];i!=-1;i=re_edges[i].next)
{
int v=edges[i].v;
int w=edges[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
{
Q.push(v);
vis[v]=1;
if(++cnt[v]>n)return false;
}
}
}
}
return true;
}
int A_star(int s,int t)
{
int cnt=0;//计数,计k短路
priority_queueQ;
if(s==t)k++;
if(dis[s]==INF)return -1;
Q.push(ANode(dis[s],0,s));
while(!Q.empty())
{
ANode temp=Q.top();Q.pop();
int u=temp.v;
if(u==t)
{
cnt++;
if(cnt==k)return temp.g;
}
for(int i=head[u];i!=-1;i=edges[i].next)
{
//移动,更新节点的估计函数
int v=edges[i].v;
int g=temp.g+edges[i].w;
int f=g+dis[v];
Q.push(ANode(f,g,v));
}
}
return -1;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=0;i
(2)2018 ACM-ICPC沈阳网络赛——D——Made in Heaven
题目链接:https://nanti.jisuanke.com/t/31445
也是一个k短路裸题,模板改一改就能过啦。
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
/*
A*求k短路模板题
*/
const int maxn=1005;
const int maxm=10000+50;
const int INF=0x3f3f3f3f;
struct Edge
{
int u;
int v;
int next;
int w;
}edges[maxm],re_edges[maxm];//re_edges为反向边
struct ANode
{
int f,g,v;//分别对应f函数,g函数和v编号
bool operator <(const ANode &rhs)const
{
if(rhs.f==f)return rhs.grhs.f;//f小的优先被考虑
}
ANode(int _f,int _g,int _v):f(_f),g(_g),v(_v){}
};
int head[maxn],re_head[maxn],tot;
void init()
{
memset(head,-1,sizeof(head));
memset(re_head,-1,sizeof(re_head));
tot=0;
}
int dis[maxn];
int vis[maxn];
int n,m,S,T,k;
void add_edges(int u,int v,int cost)
{
edges[tot].u=u;
edges[tot].v=v;
edges[tot].w=cost;
edges[tot].next=head[u];
head[u]=tot;
//反向边,因为要计算点p到最终点的最短路,就直接计算最终点到p的最短路
re_edges[tot].u=v;
re_edges[tot].v=u;
re_edges[tot].w=cost;
re_edges[tot].next=re_head[u];
re_head[u]=tot++;
}
//处理一个距离数组,表示从终点开始到各个点的最短路
int cnt[maxn];//每个点的入队次数
bool SPFA(int s)
{
for(int i=1;i<=n;i++)dis[i]=INF;
memset(vis,0,sizeof(vis));
queueQ;
while(!Q.empty())Q.pop();
vis[s]=1;
dis[s]=0;
Q.push(s);
memset(cnt,0,sizeof(cnt));
cnt[s]=1;
while(!Q.empty())
{
int u=Q.front();Q.pop();
vis[u]=0;
for(int i=re_head[u];i!=-1;i=re_edges[i].next)
{
int v=edges[i].v;
int w=edges[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!vis[v])
{
Q.push(v);
vis[v]=1;
if(++cnt[v]>n)return false;
}
}
}
}
return true;
}
int A_star(int s,int t)
{
int cnt=0;//计数,计k短路
priority_queueQ;
if(s==t)k++;
if(dis[s]==INF)return -1;
Q.push(ANode(dis[s],0,s));
while(!Q.empty())
{
ANode temp=Q.top();Q.pop();
int u=temp.v;
if(u==t)
{
cnt++;
if(cnt==k)return temp.g;
}
for(int i=head[u];i!=-1;i=edges[i].next)
{
//移动,更新节点的估计函数
int v=edges[i].v;
int g=temp.g+edges[i].w;
int f=g+dis[v];
Q.push(ANode(f,g,v));
}
}
return -1;
}
int main()
{
int time;
while(scanf("%d%d",&n,&m)!=EOF)
{
scanf("%d%d%d%d",&S,&T,&k,&time);
for(int i=0;itime)
printf("Whitesnake!\n");
else
printf("yareyaredawa\n");
}
else
printf("yareyaredawa\n");
}
return 0;
}