第k短路(A*)

题目链接Remmarguts’ Date

如果不了解A*算法可以先去看一下

 由A*算法可以直到,当目标点第一出优先队列时,一定是最优解,所以当目标点T第k次出队时,就是第k短路,这是可以用反证法证明的:

 假设这是第m次出队(m > 1) ,那么这一次出队是当前所有解中最优(A*性质),所以假设此次路径短于第m - 1次出队长度,显然上一次出队的值应该就是此次出队的值,与假设不符,故该路为第m短路

h(x)就可以采用终点到各个点的最短路作为启发式函数,因为可以保证在找第k短路的过程中某点到终点的h(x) <= g(x)

下面是poj2449的代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <deque>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <set>

using namespace std;

typedef long long ll;
typedef pair <int,int> PII;
typedef pair <int, pair <int, int> > PIII;
 
const int inf = 0x7fffffff;
const int maxn= 1e3 + 5;
const int maxm = 2e5 + 5;
const int mod = 1e9 + 7;
const int dir8[8][2] = {
   
   {-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
const int dir4[4][2] = {
   
   {-1,0},{1,0},{0,-1},{0,1}};

int n, m;
int h[maxn], rh[maxn], e[maxm], w[maxm], ne[maxm], idx;
int dist[maxn], st, ed, k;
int vis[maxn];

void add(int h[], int a, int b, int c)
{
    idx++;
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx;
}

void dij()
{
    memset(dist, 0x3f, sizeof dist);    
    priority_queue <PII, vector <PII>, greater <PII> > heap;
    dist[ed] = 0;
    heap.push({0, ed});
    while(heap.size()) {
        PII t = heap.top();
        heap.pop();
        if(vis[t.second]) continue;
        vis[t.second] = 1;
        for(int i = rh[t.second]; i; i = ne[i]) {
            int j = e[i];
            if(dist[j] > dist[t.second] + w[i]) {
                dist[j] = dist[t.second] + w[i];
                heap.push({dist[j], j});
            }
        }
    }
}

int astar()
{
    memset(vis, 0, sizeof vis);
    priority_queue <PIII, vector <PIII>, greater <PIII> > heap;
    heap.push({dist[st], {0, st}});
    while(heap.size()) {
        PIII t = heap.top();
        heap.pop();
        int ver = t.second.second, distance = t.second.first;
        vis[ver]++;
        if(vis[ed] == k) return distance;
        for(int i = h[ver]; i; i = ne[i]) {
            int j = e[i];
            heap.push({distance + dist[j] + w[i], {distance + w[i], j}});
        }
    }
    return -1;
}


int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) {
        int u, v, l;
        scanf("%d%d%d", &u, &v, &l);
        //建立反图
        add(h, u, v, l), add(rh, v, u, l);
    }
    scanf("%d%d%d", &st, &ed, &k);
    dij();
    //由于当s == t时 最短路为0,所以要求第k + 1短路
    if(st == ed) k++;
    int ans = astar();
    printf("%d\n", ans);
    return 0;
}

题目2 第k短路
因为A并不能保证除目标点之外的所有值第k次出队时都是起点到该点的第k短路(除非满足h(x)的一致性),所以上面的代码是没有进行剪枝的
但是这个题,会发现,题目和poj2449是一样的,于是选择之间提交代码,结果发现T了,根据提供的TLE的数据发现这组数据是无解的情况,在当出现无解情况时,A
算法的效率会极其低下,这时候就要对其进行剪枝,这里我们采取的策略是当某个点出队>k次时,就达到了起点到该点的第k短路,就不在入队了,从而减轻了计算时间,但是怎么能保证每一次出队时这些点都是距离最短呢?这就提到了上文中当h(x)满足一致性时,由于对启发式函数的一致性证明比较困难,在竞赛中也不容易去证明,所以在竞赛中遇到卡判重或者卡剪枝的情况,之间默认其启发式函数具有一致性即可,不需太过在意证明,当然这只是我的经验之谈… 还是希望可以完备的判断其是否具有一致性的,只是奈何能力属实不太够,也并不打算专攻图论部分…

剪枝代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <deque>
#include <string>
#include <stack>
#include <queue>
#include <map>
#include <set>

using namespace std;

typedef long long ll;
typedef pair <int,int> PII;
typedef pair <int, pair <int, int> > PIII;
 
const int inf = 0x7fffffff;
const int maxn= 1e3 + 5;
const int maxm = 2e5 + 5;
const int mod = 1e9 + 7;
const int dir8[8][2] = {
   
   {-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
const int dir4[4][2] = {
   
   {-1,0},{1,0},{0,-1},{0,1}};

int n, m;
int h[maxn], rh[maxn], e[maxm], w[maxm], ne[maxm], idx;
int dist[maxn], st, ed, k;
int vis[maxn];

void add(int h[], int a, int b, int c)
{
    idx++;
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx;
}

void dij()
{
    memset(dist, 0x3f, sizeof dist);    
    priority_queue <PII, vector <PII>, greater <PII> > heap;
    dist[ed] = 0;
    heap.push({0, ed});
    while(heap.size()) {
        auto t = heap.top();
        heap.pop();
        if(vis[t.second]) continue;
        vis[t.second] = 1;
        for(int i = rh[t.second]; i; i = ne[i]) {
            int j = e[i];
            if(dist[j] > dist[t.second] + w[i]) {
                dist[j] = dist[t.second] + w[i];
                heap.push({dist[j], j});
            }
        }
    }
}

int astar()
{
    memset(vis, 0, sizeof vis);
    priority_queue <PIII, vector <PIII>, greater <PIII> > heap;
    heap.push({dist[st], {0, st}});
    while(heap.size()) {
        auto t = heap.top();
        heap.pop();
        int ver = t.second.second, distance = t.second.first;
        vis[ver]++;
        if(vis[ed] == k) return distance;
        for(int i = h[ver]; i; i = ne[i]) {
            int j = e[i];
            if(vis[ver] > k) continue;
            heap.push({distance + dist[j] + w[i], {distance + w[i], j}});
        }
    }
    return -1;
}


int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) {
        int u, v, l;
        scanf("%d%d%d", &u, &v, &l);
        //建立反图
        add(h, u, v, l), add(rh, v, u, l);
    }
    scanf("%d%d%d", &st, &ed, &k);
    dij();
    //由于当s == t时 最短路为0,所以要求第k + 1短路
    if(st == ed) k++;
    int ans = astar();
    printf("%d\n", ans);
    return 0;
}

おすすめ

転載: blog.csdn.net/CUCUC1/article/details/113185677