1. 程式人生 > >Gym - 100781A Adjoin the Networks (樹的直徑)

Gym - 100781A Adjoin the Networks (樹的直徑)

cstring size ios scan 找到 如果 works %d 多余

題意:

n個點,m條邊,m <= n <= 100000,邊的長度都為1。

點從 0 ~ n-1 編號。開始時圖是不連通的,並且沒有環。

通過加入一些邊後,可以使圖連通。要求加入的邊不能多余(即生成的圖是一棵樹)。

問連通後的圖,任意兩點之間的距離的最大值,最小可以是多少?

技術分享圖片

技術分享圖片

既然剛開始圖不連通也無環,那麽就是一些樹(特殊情況是點)。

於是題目就變成了,如何把很多棵樹連起來,使最後生成的樹直徑最小。

可以想到,如果把兩棵直徑為 a 和 b 的樹加一條邊連成一棵,那麽直徑最小的新樹直徑為 (a+1)/2 + (b+1)/2 + 1 (兩棵樹的直徑 / 2,向上取整,的和再加 1)

選擇其中一個直徑最大的子樹,遍歷所有其他的子樹,然後將其他的子樹加到這個子樹上面。

求樹的直徑可以用DP。

這道題場上很快想出來了做法。然後一直T在 test 22。

原因是不會寫樹的直徑DP求法,以及,memset超時。

每次找到新的聯通塊(新的一棵樹)求樹的直徑就要memset一遍是不需要的。因為那些點肯定是不沖突的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include 
<cmath> #include <stack> #include <set> #include <map> using namespace std; const int maxn = 200000 + 1000; int fa[maxn], dis[maxn], tot = 0, v[maxn]; bool vis[maxn]; struct Node { int v, next, last, z; }a[maxn]; void build(int x, int y) { tot++; a[tot].v
= y; a[tot].next = a[x].last; a[x].last = tot; } void dp(int x, int &ans) { v[x] = 1; for (int tmp = a[x].last; tmp; tmp = a[tmp].next) { int y = a[tmp].v; if (v[y]) continue; dp(y, ans); ans = max(ans, dis[x] + dis[y] + 1); dis[x] = max(dis[x], dis[y] + 1); } } void DFS(int x) { vis[x] = true; for (int tmp = a[x].last; tmp; tmp = a[tmp].next) { int y = a[tmp].v; if (!vis[y]) DFS(y); } } int main() { int n, m; scanf("%d%d", &n, &m); int x, y; for (int i = 1; i <= m; i++) { scanf("%d%d", &x, &y); build(x, y); build(y, x); } memset(vis, false, sizeof(vis)); memset(v, 0, sizeof(v)); memset(dis, 0, sizeof(dis)); int q[maxn], tt = 0; for (int i = 0; i < n; i++) if (!vis[i]) { int t = 0; dp(i, t); q[++tt] = t; DFS(i); } int ans = 0, flag = 0; for (int i = 1; i <= tt; i++) if (q[i] > ans) { ans = q[i]; flag = i; } for (int i = 1; i <= tt; i++) if (i != flag) ans = max(ans, (q[i]+1)/2+(ans+1)/2 + 1); printf("%d\n", ans); return 0; }

Gym - 100781A Adjoin the Networks (樹的直徑)