P2149 [SDOI2009]Elaxia的路线 - 最短路 - 图的遍历

注意:矩阵存图,遍历边的时候首先确定边存在,即g[u][v]要有值,无值说明这条边不存在,不应该用来更新最短路

他问的是公共路径最长能有多长,就是尽量让两条路的公共路径长度之和最大,别理解错题意,比如说题目背景里面的描述是:一起走的时间尽可能的长。
看起来貌似两个人以相反的路径走过相同的一段路不算答案,但是之后替你总结好的题意是这样的:求无向图中,两对点间最短路的最长公共路径
这里是无向图,公共路径不需要方向相同。因为最短路可能有许多方案,这才导致了公共路径不是一个定值

我还是对深搜理解不够啊。。。
考虑建出只有最短路上的边的图(有向图),在这张图中可以方便地对最短路方案进行标记

只需要在深搜的回溯过程中记录就好,因为可能从x出发的某最条短路边并不是最短路方案(s ~ t)上的边,但是从t回溯回去的边一定是最短路方案上的边

有两种可能出现公共路径,相遇或者同行,一个最长公共路径里面不可能是两个人既相遇(相遇是指都走一条路但是方向相反)又同行了一段路

考虑两对点是在同一张图上面求的最短路,在公共路径上,要么他们方向完全相反,要么就相同,如果说一个人的方向有一段是和另一个人相同,又有一段是相反,这种路径就应该不是最短路,因为有相同方向又有相反方向,就感觉这个人是刻意绕路

既然他最后反正都要和另一个人方向相同,走的又是同一段路(公共路径!)他为什么还要和他反着走?

所以求两遍,随便以一个人的起点出发,第一次找同行最长公共路径,第二次找相遇最长公共路径

注意写一些记忆化。。。
这里是有技巧的。。。图的遍历,判重复走过的写法一般有两种

void dfs(int x) {
    vis[x] = 1;
    for(int i=head[x]; i; i=e[i].to) {
        int v = e[i].v;
        if(vis[v]) continue;
        calc();
        dfs(v);
    }
}

上面这种写法,适用于不能重复统计一个点的情况

void dfs(int x) {
    if(vis[x]) return;
    vis[x] = 1;
    for(int i=head[x]; i; i=e[i].to) {
        int v = e[i].v;
        calc();
        dfs(v);
    }
}

这种写法,适合需要不重复走已经走过的路,而确实需要统计这个点(更多时候是点所连的边)的情况
你会发现这道题需要用第二种写法,因为当你搜到一个已经走过的点,这条边若为公共边,你仍需要统计这条边的权值,如果用第一种写法,搜到这个点,发现曾经走过,直接continue,calc()直接被略过,导致标记最短路,统计权值通通都被跳过了。但是第二种写法,直到进入了v的递归层才会退出,没进递归层之前的操作仍然做了一遍

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 2500 + 10;
typedef long long ll;
int n,m,flg[MAXN],vis[MAXN],g[MAXN][MAXN],dis[MAXN],ans,x1,y1,x2,y2,g1[MAXN][MAXN],g2[MAXN][MAXN];

struct st{
    int id, d;
    bool operator < (const st &a) const{
        return d > a.d;
    }
};
priority_queue<st> q;

void dfs_init(int x, int flg_tag, int t) {
    if(x == t) {
        flg[x] = 1;
        return;
    }
    if(vis[x]) return;
    vis[x] = 1;
    for(int i=1; i<=n; i++) {
        int d = g[x][i];
        if(!d) continue;
        if(dis[x] + d == dis[i]) {
            dfs_init(i, flg_tag, t);
            if(flg[i]) {
                flg[x] = 1;
                if(flg_tag == 1) g1[x][i] = d;
                else g2[x][i] = d;  
            }
        }
    }
}

void work(int s, int flg_tag, int t) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));    
    dis[s] = 0;
    q.push((st){s, 0});
    while(!q.empty()) {
        st temp = q.top();
        q.pop();
        int x = temp.id, d = temp.d;
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i=1; i<=n; i++) {
            if(g[x][i]) {
                if(dis[i] > d + g[x][i]) {
                    dis[i] = d + g[x][i];
                    q.push((st){i, dis[i]});
                }
            }
        }
    }
    memset(flg, 0, sizeof(flg));
    memset(vis, 0, sizeof(vis));
    dfs_init(s, flg_tag, t);
}

void dfs(int x, int now, int tag) {
    ans = max(ans, now);
    if(vis[x]) return;
    vis[x] = 1;
    for(int i=1; i<=n; i++) {
        int d = g1[x][i];
        if(!d) continue;
        if(tag == 1) {
            if(g2[x][i]) {
                dfs(i, now + g2[x][i], tag);
            } else {
                dfs(i, now, tag);
            }
        } else {
            if(g2[i][x]) {
                dfs(i, now + g2[i][x], tag);
            } else {
                dfs(i, now, tag);
            }
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    for(int i=1; i<=m; i++) {
        int u,v,l;
        scanf("%d%d%d", &u, &v, &l);
        g[u][v] = g[v][u] = l;
    }
    work(x1, 1, y1), work(x2, 2, y2);
    
    memset(vis, 0, sizeof(vis));
    dfs(x1, 0, 1);
    
    memset(vis, 0, sizeof(vis));
    dfs(x1, 0, 2);
    
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zolrk/p/9822875.html
今日推荐