POJ2449-Remmarguts' Date(K短路)

题目链接

http://poj.org/problem?id=2449

题意

求一个有向图的给定起点和终点的K短路

思路

A* + SPFA
A*算法的核心是估价函数f(n) = g(n) + h(n)的设计。
对于该题,设g(n)为当前点到起点的最近距离,h(n)为当前点到终点的最近距离,f(n) = h(n) + g(n)的第k大即为起点到终点的第K短路。
该题为有向图,于是先将所有边反向,从终点做一次单源最短路径,求出h(n)。在从起点单源最短路径,原来的d[]数组实际上就是估价函数f()。当终点第K次从优先队列中出来的时候得到的就是第K短路

细节

  1. 该图为有向图,因此求每个点到终点的最短路时,从终点做单源最短路径时应将所有边反向
  2. 起点和终点相同的时候,因为该题默认必须要经过边,所以要K++

证明

在图上,只要h(n)满足一致性,那么A*算法得到的一定是最优解
定义:
f(n):从初始状态到目标状态的估计代价
g(n):从初始状态到目前状态的实际代价
h(n):从目前状态到目标状态的估计代价
h(n)的一致性:设v为u的后继节点,则h(u) ≤ h(v) + w(u, v) (w(u, v)为u到v的代价)
该题的证明
首先证明该题中h(n)满足一致性:
在该题中,h(n)定义为当前节点u到目标节点t的最短路径,v为u的后继结点,那么只需要证明:h(u) ≤ h(v) + w(u, v)
反证法,假设h(u) > h(v) + w(u, v)。那么求t到u的最短路径时,h(u)可以直接更新为h(v) + w(u, v),即h(u)不是t到u的最短路径,矛盾。因此h(n)满足一致性定理
其次再证明在图搜索上,只要h(n)满足一致性,A*算法产生的即为最优解
I.证明沿着路径上扩展的f(n)一定是非递减的:
设v为u的后继结点,则f(v) = g(v) + h(v) = g(u) + w(u, v) + h(v) ≥ g(u) + h(u) = f(n)
II.证明第一次选择扩展目标节点的时候得到的是s到t的最优解
反证法, 假设存在另一个结点t’,并且t’更优,那么选择扩展的时候,一定先扩展t’而不是t,因此第一次扩展目标节点t的时候得到的是t的最优值
那么我们就得到了第一次扩展t的时候得到的是s到t的最短路,那么同理,第k次扩展到目标节点的时候得到的是第k短路,得证


代码

#include<iostream>
#include<cstring>
#include<stack>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#include<sstream>
#include<iomanip>
#include<fstream>
#include<cstdio>
#include<cstdlib>
#include<climits>
#include<deque>
using namespace std;

#define INF 10000000
#define PI acos(-1.0)
#define LL long long
#define PII pair<int, int>
#define PLL pair<LL, LL>
#define mp make_pair
#define IN freopen("in.txt", "r", stdin)
#define OUT freopen("out.txt", "wb", stdout)
#define scan(x) scanf("%d", &x)
#define scan2(x, y) scanf("%d%d", &x, &y)
#define sqr(x) (x) * (x)

const int maxn = 1000 + 5;
int N, M;
struct Edge {
    int from, to, dist;
    Edge(int u, int v, int w) : from(u), to(v), dist(w) {

    }
};

vector<Edge> edges;
vector<Edge> redges;
vector<int> G[maxn];
vector<int> RG[maxn];

void addedge(int u, int v, int w) {
    edges.push_back(Edge(u, v, w));
    int s = edges.size() - 1;
    G[u].push_back(s);
    redges.push_back(Edge(v, u, w));
    s = redges.size() - 1;
    RG[v].push_back(s);
}

int h[maxn];
void spfa(int s) {
    int inq[maxn];
    memset(inq, 0, sizeof(inq));
    queue<int> q;
    for (int i = 1; i <= N; i++) h[i] = INF;
    h[s] = 0;
    inq[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = 0;
        int s = RG[u].size();
        for (int i = 0; i < s; i++) {
            Edge &e = redges[RG[u][i]];
            if (h[u] < INF && h[e.to] > h[u] + e.dist) {
                h[e.to] = h[u] + e.dist;
                if (!inq[e.to]) {
                    inq[e.to] = 1;
                    q.push(e.to);
                }
            }
        }
    }
}

struct node {
    int _f, _g, _v;
    node (int x, int y, int z) : _f(x), _g(y), _v(z) {

    }
};

struct cmp {
    bool operator() (node x, node y) {
        return x._f > y._f;
    }
};

int Astar(int S, int T, int K) {
    if (h[T] == INF) return -1;
    if (S == T) K++;
    int cnt[maxn], shortest;
    memset(cnt, 0, sizeof(cnt));
    priority_queue<node, vector<node>, cmp> q;
    q.push(node(h[S] , 0, S));
    while (!q.empty()) {
        node t = q.top();
        q.pop();
        int u = t._v;
        cnt[u]++;
        if (u == T && cnt[T] == K) return t._f;
        int s = G[u].size();
        for (int i = 0; i < s; i++) {
            Edge &e = edges[G[u][i]];
            int g = t._g + e.dist;
            int f = h[e.to] + g;
            q.push(node(f, g, e.to));
        }
    }
    return -1;
}

int main() {
    scan2(N, M);
    for (int i = 0; i < M; i++) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        addedge(x, y, z);
    }
    int S, T, K;
    scanf("%d%d%d", &S, &T, &K);
    spfa(T);
    printf("%d\n", Astar(S, T, K));
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值