【DFS+思維 無根樹】Gym 101673F Keeping On Track
阿新 • • 發佈:2018-11-06
Gym 101673F Keeping On Track
題意:給你一棵樹(注意是棵樹),讓你求斷開一個點後不同聯通塊組成的最大結點對數,然後求新增一條邊後剩下的最少的不同聯通塊組成的結點對數。
如果暴力的去搜斷開每個點的節點對數的複雜度是O(n^2)會TLE。
所以我們要利用回溯實現O(n)的dfs。然後再記錄一下最大的兩個聯通塊數量就行了。
#include <bits/stdc++.h> using namespace std; bool vis[10005]; vector <int> G[10005]; int k = -1, maxx = -1; //k是儲存炸燬的城市 maxx是炸燬的點對 int n; int dfs(int x) { vis[x] = 1; int tot = 0, sum = 0; for(int i = 0; i < G[x].size(); i++) { int v = G[x][i]; if(!vis[v]) { int t = dfs(v); sum += t * (n - t); //加上刪除此點後,兩邊的數量乘積 tot += t; } } sum += tot * (n - tot); //因為下面的會有重複計算,所以加上上面的,直接除2 sum /= 2; if(sum > maxx) { maxx = sum; //得到最大炸燬對數和炸燬的點 k = x; } return tot + 1; } int dfs2(int x) { vis[x] = 1; int tot = 1; for(int i = 0; i < G[x].size(); i++) { int v = G[x][i]; if(!vis[v]) { tot += dfs(v); //得到連通塊數 } } return tot; } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(0); //以0為根節點遍歷 memset(vis, 0, sizeof vis); vis[k] = 1; //因為k是炸燬的城市,所以vis為1 int ma1 = 0, ma2 = 0; //最大和次大連通塊內城市數量 for(int i = 0; i < G[k].size(); i++) { int v = G[k][i]; if(!vis[v]) { int p = dfs2(v); if(p > ma1) //儲存最大和次大連通塊 { ma2 = ma1; ma1 = p; } else if(p > ma2) { ma2 = p; } } } printf("%d %d\n", maxx, maxx - ma1 * ma2); return 0; }