POJ 2449 Remmarguts' Date(A* + spfa)

题目分析

本题就是求k短路,其实自己也没有学过A*算法 f[x]=g[x]+h[x]
在本题中g[x]为估价函数,表示从x点达到目标点的距离,注意g[x],小于等于真实值,因此这样就可以用spfa跑一边最短路即可,至于h[x]表示到达当前点x的代价,很明显这个在运算的时候可以得到,这样就可以构建一个优先队列求解了。h[x]+g[x]小的先出队,并且用一个计数的计算出每个点被到达多少次。这样就可以得出结果了。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1005;
const int maxm = 100005;
const int INF = 0x7f;

struct Node{
    int h, g, u;  //h代表当前搜索时的代价, g是一个估价函数(注意g一定要小于或者等于真实值)
    Node(){}
    Node(int a,int b,int c):h(a),g(b),u(c){}
    bool operator < (const Node temp)const{  //重载小于运算符
        return (h+g) > (temp.h+temp.g);
    }
};

int head1[maxn], head2[maxn], g[maxn], vis[maxn], tot1, tot2;

struct Edge{
    int to, w, next;
}e1[maxm], e2[maxm]; //正向和反向分别建边,反向建边主要是为了能求出估价函数g(spfa或者dijkstra都可)

void addedge(int from, int to, int w){
    e1[tot1].to = to;
    e1[tot1].w = w;
    e1[tot1].next = head1[from];
    head1[from] = tot1++;
    e2[tot2].to = from;
    e2[tot2].w = w;
    e2[tot2].next = head2[to];
    head2[to] = tot2++;
}

void spfa(int s){
    memset(vis, 0, sizeof(vis));
    memset(g, INF, sizeof(g));
    queue <int> que;
    g[s] = 0, vis[s] = 1;
    que.push(s);
    while(!que.empty()){
        int u = que.front(); que.pop();
        vis[u] = 0;
        for(int i = head2[u]; i != -1; i = e2[i].next){
            int v = e2[i].to, w = e2[i].w;
            if(g[v] > g[u] + w){
                g[v] = g[u] + w;
                if(!vis[v]){
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
}

void solve(int s, int t, int k){
    memset(vis, 0, sizeof(vis));
    if(s == t) k++;  //注意这一点
    priority_queue <Node> pq;
    pq.push(Node(0, g[s], s));
    while(!pq.empty()){
        Node cur = pq.top(); pq.pop();
 //       printf("%d  %d  %d\n", cur.h, cur.g, cur.u);
        vis[cur.u]++;
        if(vis[cur.u] > k) continue;
        if(vis[cur.u] == k && cur.u == t){
            printf("%d\n", cur.h+cur.g);
            return ;
        }

        for(int i = head1[cur.u] ; i != -1; i = e1[i].next){
            int v = e1[i].to, w = e1[i].w;
            pq.push(Node(w+cur.h, g[v], v));
        }
    }
    printf("-1\n");
}

void init(){
    tot1 = tot2 = 0;
    memset(head1, -1, sizeof(head1));
    memset(head2, -1, sizeof(head2));
}

int main(){
    int N, M, S, T, K;
    while(scanf("%d%d", &N, &M) != EOF){
        init();
        int from, to, w;
        for(int i = 0; i < M; i++){
            scanf("%d%d%d", &from, &to, &w);
            addedge(from, to, w);
        }
        scanf("%d%d%d", &S, &T, &K);
        spfa(T);
        if(g[S] == INF){
            printf("-1\n");
            continue;
        }
        solve(S, T, K);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值