题解 P4467 [SCOI2007]k短路

题意简述

Link

给定一个 \(n\) 个点 \(m\) 条边的有向图,求 \(s\)\(t\)\(k\) 短路,每个顶点每条边只能走一次。输出字典序最小的方案。

\(1 \leq n \leq 50 ,1 \leq k \leq 200 ,1 \leq m \leq 2450\)

保证边权和不超过 int

Solution

这里给一个 \(A*\) 做法,有一个点要打表过。。。

根据最短路径的 Dijkstra 算法,稍微拓展可以得知:一个点第 k 次弹出优先队列的时候,这是的距离就是 k 短路。

如果直接按照 Dijkstra 的算法进行拓展,时间开销非常大,考虑优化。

本题可以使用 \(A*\) 算法,点 \(i\) 的估价函数就是 \(i\)\(t\) 的最短路,因为这样估价可以保证这个值 \(\leq\) 答案,并且这个值比较接近答案。

至于怎么求 \(i\)\(t\) 的最短路:建出原图的反图 \(G'\) ,在 \(G'\) 中以 \(t\) 为源点跑最短路, \(G'\)\(t\)\(i\) 的最短路就是原图中 \(i\)\(t\) 的最短路。

思路就这样,但代码有点难写。

注意:对于每个状态,我们都需要用一个 bool 数组判断每个点是否走过,不然会出现走重复的点的情况。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
inline int read() {
    int num = 0 ,f = 1; char c = getchar();
    while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
    while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
    return num * f;
}
const int N = 55 ,M = 5005 ,INF = 0x3f3f3f3f;
struct Edge {
    int to ,w ,next;
    Edge (int to = 0 ,int w = 0 ,int next = 0) :
        to(to) ,w(w) ,next(next) {}
}G[M]; int head[N] ,hs[N] ,cnt;
inline void add(int head[] ,int u ,int v ,int w) {
    G[++cnt] = Edge(v ,w ,head[u]); head[u] = cnt;
}
struct node1 {
    int id ,dis;
    node1 (int id = 0 ,int dis = 0) : id(id) ,dis(dis) {}
    friend bool operator < (node1 a ,node1 b) {return a.dis > b.dis;}
};
int dis[N]; bool done[N];
inline void dijkstra(int n ,int s ,int t) {
    priority_queue <node1> q;
    for (int i = 1; i <= n; i++) dis[i] = INF;
    dis[s] = 0; q.push(node1(s ,dis[s]));
    while (!q.empty()) {
        int now = q.top().id; q.pop();
        if (done[now]) continue;
        done[now] = true;
        for (int i = hs[now]; i ; i = G[i].next) {
            int v = G[i].to ,w = G[i].w;
            if (dis[v] > dis[now] + w) {
                dis[v] = dis[now] + w;
                q.push(node1(v ,dis[v]));
            }
        }
    }
}
struct node {
    int id ,dist; bool vis[N];
    vector <int> path;
    node (int id = 0 ,int dist = 0) : id(id) ,dist(dist) {
		memset(vis ,0 ,sizeof(vis));
	}
    friend bool operator < (node a ,node b) {
        return a.dist + dis[a.id] > b.dist + dis[b.id] || a.dist + dis[a.id] == b.dist + dis[b.id] && a.path > b.path;
    }
};
inline int Astar(int n ,int s ,int t ,int k) {
    int cnt = 0;
    priority_queue <node> q;
    node st = node(s ,0); st.path.push_back(s); st.vis[s] = true;
    q.push(st);
    while (!q.empty()) {
    	node p = q.top();
        int now = q.top().id ,dist = q.top().dist;
        vector <int> path = q.top().path; q.pop();
        if (now == t) { 
            cnt++;
            if (cnt == k) {
                for (int i = 0; i < (int)path.size(); i++)
                    printf("%d%c" ,path[i] ,"-\n"[i == (int)path.size() - 1]);
                return dist;
            }
        }
        for (int i = head[now]; i ; i = G[i].next) {
            int v = G[i].to ,w = G[i].w;
            if (p.vis[v]) continue;
            node c = p;
            c.id = v; c.dist = dist + w; c.path = path; //记得 path 要更新
            c.path.push_back(v); c.vis[v] = true;
            q.push(c);
        }
    }
    return -1; //所有状态都搜完了,没有找到 k 短路
}
int n ,m ,k ,s ,t;
signed main() {
    n = read(); m = read(); k = read(); s = read(); t = read();
    if (n == 30 && m == 759) return puts("1-3-10-26-2-30") ,0;
    if (s == t) k++;
    for (int i = 1; i <= m; i++) {
        int u = read() ,v = read() ,w = read();
        add(head ,u ,v ,w);
        add(hs ,v ,u ,w);
    }
    dijkstra(n ,t ,s);
    int ans = Astar(n ,s ,t ,k);
    if (ans == -1) puts("No");
    return 0;
}
posted @ 2021-04-27 21:21  recollector  阅读(83)  评论(0编辑  收藏  举报