2011.07.16

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的题解~~我觉得你的代码总是让人一目了然,太强了!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值