P2149 [SDOI2009]Elaxia的路线 - 最短路 - 图的遍历
Posted zolrk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
", ans);
return 0;
}
以上是关于P2149 [SDOI2009]Elaxia的路线 - 最短路 - 图的遍历的主要内容,如果未能解决你的问题,请参考以下文章
洛谷—— P2149 [SDOI2009]Elaxia的路线