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;
}
P2149 [SDOI2009]Elaxia的路線 - 最短路 - 圖的遍歷