POJ 2449 Remmarguts' Date
题意:求出s到t的第k短路
k短路模板题,在逆边图上用dijstra得出最短路,作为A*中的h(x):估价函数的下界,然后用f(x)=g(x)+h(x)维护一个优先队列,每个点第k次如队列时得到的就是这个点到终点的最短路。显然k次以上点入队列是没意义的,故减去。
类似dijkstra可以确定下面几个搜索策略(事实上dijkstra就是取h(x)=0的A*搜索特例):
1、用优先队列保存节点进行搜索。
2、放开每个节点的入队次数,求k短路,每个节点可以入队k次。
首先看第一个策略,在A*算法中用优先队列就是要用到启发函数f(s)确定状态在优先队列里面的优先级。其实Dijkstra用到的优先队列实际上就是估价函数值为0,启发函数f(s)=g(s),即是选取到源点距离最近的点进行扩展。因为h(s)=0满足了估价函数相容这个条件。这题求k短路就不能单纯的使用h(s)=0这个估价函数。解决这道题的时候选取h(x)=dt(x), dt(x)是x节点到目标节点的最短距离。最短距离可以开始由Dijkstra直接求得。
再看第二个策略,控制每个节点的入队(或出队)次数为k次,可以找到第k短路径。可能这样想有点主观的套用,那么我就先来证明这样一个结论:
如果x是s到t的第k短路径上的一个节点,那么由这条路径s到x是s到x的第m短路径,则不可能有m>k。用反证法很容易得出:如果这条路径是s到x的第m短路径,如果m>k,那么经过x到t的路径就有m-1条比当前路径要短,不符合当前路径是s到t的第k短路径。
#include<cstdio>
#include<cstring>
#include<queue>
#define MAXE 100001
#define MAXV 1001
using namespace std;
const int INF=(int)1<<31-1;
typedef pair<int,int> pii;
int u,v,w,t,s,m,n,k,V,E,pos1,pos2,head1[MAXV],head2[MAXV];
int ans,h[MAXV],d[MAXV];
bool cannot=0;
struct Edge
{
int w,next,v;
}node1[MAXE*2],node2[MAXE*2];
struct node
{
int dis,id;
bool operator<(const node &A)const{
return dis+h[id]>A.dis+h[A.id];
}
};
void add1(int u,int v,int w)
{
node1[pos1].v=v;
node1[pos1].w=w;
node1[pos1].next=head1[u];
head1[u]=pos1++;
}
void add2(int u,int v,int w)
{
node2[pos2].v=v;
node2[pos2].w=w;
node2[pos2].next=head2[u];
head2[u]=pos2++;
}
void dijkstra(int s)
{
for(int i=0;i<=V;i++) h[i]=INF;h[s]=0;
priority_queue<pii,vector<pii>,greater<pii> > q;
bool done[MAXV];
memset(done,0,sizeof(done));
q.push(make_pair(h[s],s));
while(!q.empty()){
pii u=q.top();q.pop();
int x=u.second;
if(done[x]) continue;
done[x]=true;
for(int i=head1[x];i!=-1;i=node1[i].next) //松弛操作
if(h[node1[i].v]>h[x]+node1[i].w){
h[node1[i].v]=h[x]+node1[i].w;
q.push(make_pair(h[node1[i].v],node1[i].v));
}
}
}
void A_star(int s)
{
int kshort[MAXV];
memset(kshort,0,sizeof(kshort));
priority_queue<node> q;
node start; start.id=s;start.dis=0;
q.push(start);
while(!q.empty()){
node u=q.top();q.pop();
int dis=u.dis,x=u.id;
kshort[x]++;
if(kshort[t]==k){
printf("%d\n",dis);
return ;
}
if(kshort[x]>k) continue; //剪
for(int i=head2[x];i!=-1;i=node2[i].next){
node temp;
temp.id=node2[i].v;temp.dis=dis+node2[i].w;
q.push(temp);
}
}
printf("-1\n");
}
int main()
{
freopen("in","r",stdin);
freopen("out","w",stdout);
scanf("%d%d",&V,&E);
pos1=pos2=1;
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
for(int i=0;i<E;i++){
scanf("%d%d%d",&u,&v,&w);
add1(v,u,w);
add2(u,v,w);
}
scanf("%d %d%d",&s,&t,&k);
if(s==t) k++;
dijkstra(t);
A_star(s);
return 0;
}
再次感谢boat的题解~~我觉得你的代码总是让人一目了然,太强了!