日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【每日一题】7月7日题目精讲—最短路

發布時間:2023/12/3 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【每日一题】7月7日题目精讲—最短路 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

來源:牛客網:

文章目錄

    • 題目描述
    • 題解:
    • 代碼:

時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 1048576K,其他語言2097152K 64bit IO Format: %lld

題目描述

給一個連通圖,每次詢問兩點間最短路。每條邊的長度都是1。 輸入描述: 第一行兩個整數n和m,表示圖的點數和邊數(1≤ n≤ 100000,
1≤ m≤ n+100)。 接下來m行每行兩個整數a和b,表示一條邊(1≤ a, b≤ n)。保證沒有自環和重邊。保證圖連通。
接下來一個整數q表示詢問的個數(1≤ q≤ 100000)。 接下來q行每行兩個整數a和b表示詢問a和b之間的最短路。

輸出描述:

每個詢問輸出一行表示答案。

示例1
輸入

4 5 1 2 2 3 1 4 4 3 2 4 4 1 4 1 2 2 4 1 3

輸出

1 1

題解:

我看了別人的講解才逐漸明白。。我太菜了
題目是最短路,題目內容也是最短路,但是解法卻不是常用的spfa等,因為詢問的個數有點多(1 ~ 100000)
我們仔細看數據范圍,m<n+100,什么意思?想想m = n-1時是一棵樹,那我們就可以把他當做樹處理,然后剩下的邊再慢慢干
如果當做一棵樹的話,邊長為1,求最短路徑,我們就可以通過最近公共祖先(lca)得到兩點的最近距離,dep[a]+dep[b] - 2dep [ lca(a, b) ] (a的深度+b的深度,然后a和b有重復的部分,減去重復的部分)
然后我們看看多出來的100個邊,會對結果有什么影響?

藍色是原本的樹,橙色,綠色是多出來的邊
如果是橙色,對結果沒有影響,如果是綠色會有影響
那么該如何處理?
我們可以把剩下多出來的邊跑單元最短路(以這些邊的一個端點開始)并記錄下來
然后與原路徑進行比較
a到b的最小距離就在dep[a] +dep[b]-2dep[ lca(a,b) ]與dis[a[i]]+dis[b[i]]中取最小值
不知道有沒有聽明白,我拿樣例做個分析:
我們看一下樣例,

1 2 2 3 1 4 4 3 2 4

多余的邊是:1-2 , 4-3,

ans最開始的值就是樹上的lca
ans={1,2,1,3}
然后開始跑1-2這個邊,從1這個點開始,計算出1到個點的距離
dis={0,1,2,1}
然后更新最短距離:
ans[i] = min(ans[i], dis[a[i]] + dis[b[i]]);
a和b分別表示詢問中a和b的距離
a[2]=1,b[2]=2
dis[a[2]]+dis[b[2]]=0+1=1<ans[2]
所以ans[2]=1
大致就是這個過程

代碼:

代碼來自

#include<bits/stdc++.h> using namespace std; #define fi first #define se second #define pb push_back #define mp make_pair #define all(x) (x).begin(), (x).end() #define endl '\n' #define SZ(x) (int)x.size() typedef long long ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int mod = 1e9+7; //const int mod = 998244353; const double eps = 1e-10; const double pi = acos(-1.0); const int maxn = 1e6+10; const ll inf = 0x3f3f3f3f; const int dir[][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; int n, m, depth[maxn], f[maxn][50]; int from[maxn], to[maxn << 1], nxt[maxn << 1], cnt = 1, Log[maxn], From[maxn]; bool vis[maxn], used[maxn]; //鏈式前向星加邊 void addEdge (int u, int v) {From[++cnt] = u, to[cnt] = v, nxt[cnt] = from[u], from[u] = cnt; } //計算深度&計算祖先 void dfs (int u, int fa) {depth[u] = depth[fa] + 1;vis[u] = 1;for (register int i = 1; i <= Log[n]; ++i) {if ((1 << i) > depth[u]) break;f[u][i] = f[f[u][i - 1]][i - 1];}for (register int i = from[u]; i; i = nxt[i]) {ll v = to[i];if (vis[v]) continue;used[i] = used[i ^ 1] = 1;f[v][0] = u;dfs (v, u);} } //計算LCA inline int LCA (int x, int y) {if (depth[x] < depth[y]) swap(x, y);//我們默認x為更深的那個點for(register int i = Log[n] ; i >= 0 ; --i)if(depth[x] - (1 << i) >= depth[y]) x = f[x][i];//將x跳到和y同一深度上if (x == y) return x;for (register int i = Log[n]; i >= 0; --i)if (f[x][i] != f[y][i])x = f[x][i], y = f[y][i];//一起向上跳return f[x][0];//不難看出,此時兩個點均在其LCA的下方,往上跳一次即可 } void init(){Log[0] = -1;for (register int i = 1, u, v; i <= m; ++i) {cin >> u >> v;addEdge (u, v); addEdge(v, u);Log[i] = Log[i >> 1] + 1;}Log[n] = Log[n >> 1] + 1;dfs(1, 0); } int dist(int p , int q){return depth[p] + depth[q] - 2 * depth[LCA(p , q)];} int ans[maxn],a[maxn],b[maxn],dis[maxn], Q, q[maxn], h, t; void bfs(int s){for (int i = 1; i <= n; i++)dis[i] = inf;dis[s] = 0;h = t = 0;q[++h] = s;while (t < h) {int u = q[++t];for (int i = from[u]; i; i = nxt[i]){int v = to[i];if (dis[v] > dis[u] + 1)dis[v] = dis[u] + 1, q[++h] = v;}}for (int i = 1; i <= Q; i++)ans[i] = min(ans[i], dis[a[i]] + dis[b[i]]); } int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout);cin >> n >> m;init();cin >> Q;for (int i = 1; i <= Q; i++){cin >> a[i] >> b[i];ans[i] = dist(a[i], b[i]);}int num = 0;for (int i = 2; i <= cnt; i++)if(!used[i]) {used[i] = used[i ^ 1] = 1;bfs(From[i]);num++;if(num > 101) break;}for (int i = 1; i <= Q; i++)cout << ans[i] << endl;return 0; }

總結

以上是生活随笔為你收集整理的【每日一题】7月7日题目精讲—最短路的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。