次短路-POJ3255

思路

这是严格次短路。(题目数据有点水。。。。。)
到某个顶点v的次短路要么是其他某个顶点u的最短路再加上e(u,v),要么是到u的次短路再加上e(u,v)的边。因此对于每个顶点,我们记录的不仅仅的最短距离,还有次短距离。
考虑在什么情况下会更新最短路。(可重复走同一条边)
1、由父亲节点过来的距离小于最短路,那么当前最短路变成次短路,更新最短路
2、若当前距离不能更新最短路,但比次短路小,更新次短路
3、若从父亲节点过来的次短路能更新当前次短路,更新次短路
所以,求次短路只需要在更新最短路的时候顺便更新次短路就好了

模拟,可帮助理解上述黑体加粗

例题 POJ3255

送上一个案例
4 6
1 2 1
1 2 5
1 3 2
2 3 2
2 4 1
2 4 6

解法一:dij变形

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> iip;
typedef pair<ll, ll> llp;
const int MAXN = 100005;
const int MAXM = 100005;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;

int i, j, n, m, a, b, d, cnt, dis[MAXN], dis2[MAXN], head[MAXN];
struct edge{
    int u;
    int v;
    int w;
    int next;
    edge(){};
    edge(int _u, int _v, int _w, int _next) : u(_u), v(_v), w(_w), next(_next) {};
}e[2*MAXM];

void init() {
    cnt = 0;
    for(i = 0; i <= n; i++) {
        dis[i] = INF;
        dis2[i] = INF;
        head[i] = -1;
    }
}

void addEdge(int u, int v, int w) {
    e[cnt] = edge(u, v, w, head[u]);
    head[u] = cnt++;
}

void dij(int s) {
    dis[s] = 0;
    priority_queue<iip, vector<iip>, greater<iip> > pq;
    pq.push({dis[s], s});
    while(!pq.empty()) {
        iip now = pq.top();
        pq.pop();
        int u = now.second;
        int d = now.first;
        if(dis2[u] < d)  continue;
        for(i = head[u]; i != -1; i = e[i].next) {
            int d2 = d + e[i].w;//注意此处与最短路的区别,取出的当前最小距离不一定是离起始点的最短距离,还可以是次小距离!所以不能用 d2 = dis[u] + e[i].w;
            int v = e[i].v;
            if(dis[v] > d2) {//如果父亲结点过来的距离小于最短路
                swap(dis[v], d2);//那么当前最短路变成次短路,更新最短路
                pq.push({dis[v], v});
            }
            if(dis2[v] > d2 && dis[v] < d2) {//若当前距离不能更新最短路,但比当前次短路小;或者从父亲结点过来的次短路比当前次短路小
                dis2[v] = d2;//更新次短路
                pq.push({dis2[v], v});
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    while(m--) {
        scanf("%d%d%d", &a, &b, &d);
        addEdge(a, b, d);
        addEdge(b, a, d);
    }
    dij(1);
    printf("%d\n", dis2[n]);
    return 0;
}

解法二:利用第k短路算法

这题数据还是很弱的,以下第 k 短路算法并没有遵守题意中的严格次短路还是AC了,读者可自行测试数据:
4 4
1 2 1
1 3 1
2 4 1
3 4 1
解法一:ans = 4;
解法二:ans = 2;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> llp;
typedef pair<int, int> iip;
const int MAXN = 200005;
const int MAXM = 200005;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;

int i, j, n, m, cnt, a, b, d, head[MAXN], dis[MAXN], num = 0;

struct edge{
    int u, v, w, next;
    edge(){};
    edge(int _u, int _v, int _w, int _next) : u(_u), v(_v), w(_w), next(_next) {};
}e[MAXM];

struct node{
    int g, v;//g(n)起点到顶点v的实际距离
    node(){};
    node(int _g, int _v) : g(_g), v(_v) {};
    bool operator < (const node &x) const{
        return g + dis[v] > x.g + dis[x.v];//f(n) = g(n) + h(n)
    }
};

void init() {
    cnt = 0;
    for(i = 0; i <= n; i++) {
        dis[i] = INF;
        head[i] = -1;
    }
}

void addEdge(int u, int v, int w) {
    e[cnt] = edge(u, v, w, head[u]);
    head[u] = cnt++;
}

void dij(int s) {
    priority_queue<iip, vector<iip>, greater<iip> > pq;
    dis[s] = 0;
    pq.push({dis[s], s});
    while(!pq.empty()) {
        iip now = pq.top();
        pq.pop();
        int u = now.second;
        if(dis[u] < now.first) continue;
        for(i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(dis[u] != INF && dis[u] + e[i].w < dis[v]) {
                dis[v] = dis[u] + e[i].w;
                pq.push({dis[v], v});
            }
        }
    }

}

int Astar(int s, int t, int k) {
    if(dis[s] ==  INF)  return -1;//不存在第k短路
    priority_queue<node> pq;//根据估值函数搜索
    if(s == t)  k++;//起点和终点相同时,需绕一圈再回来
    pq.push(node(0, s));
    while(!pq.empty()) {
        node now = pq.top();
        pq.pop();
        if(now.v == t) num++;//若为严格第k短路,可用set存每次到达终点的距离,根据set的大小来判断是否到达严格第k短路
       if(num == k)    return now.g;//走到第k短路时,返回实际距离
        for(i = head[now.v]; i != -1; i = e[i].next) {
            pq.push(node(now.g + e[i].w, e[i].v));//g(n) = 父亲结点的 g + 这条边的距离
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    while(m--) {
        scanf("%d%d%d", &a, &b, &d);
        addEdge(a, b, d);
        addEdge(b, a, d);
    }
    dij(n);//h(n),各个顶点到终点n的估计距离(最短距离),有向图需反向
    int ans = Astar(1, n, 2);
    printf("%d\n", ans);
    return 0;
}

参考文章1
参考文章2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343