無向圖 N 點 M 邊。刪除 2 個點後,求剩餘圖中最大連通分量的點數最大值。輸出該最大值。
cut[u])
// 第一次 Tarjan:求割點及 cut[u]
int dfs(int u, int fa) {
int lowu = dfn[u] = ++dfs_clock;
int child = 0;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (v == del) continue; // 第二次用:忽略已刪除點
if (!dfn[v]) {
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if (lowv >= dfn[u]) cut[u]++; // u 是割點,這條邊對應一個新分量
} else if (dfn[v] < dfn[u]) {
lowu = min(lowu, dfn[v]);
}
}
if (fa == -1 && child == 1) cut[u] = 0; // 根特判
return lowu;
}
// 第二次:枚舉第一個刪除點 u (必須是割點)
for (int u = 1; u <= n; u++) if (cut[u] > 0) {
del = u;
memset(dfn, 0, sizeof(dfn));
dfs_clock = 0;
// 再次 Tarjan,但忽略 u,求在 u 被刪除後,再刪一個點的最大效果
// 結合 cut[u] 和第二次結果計算答案
}
實際上,刪除兩點後的最大連通分量 = max(
cut[u] + 1 (刪除 u 後的分量數)但 n≤5000,可 O(n·(n+m)) 暴力枚舉第一點 + Tarjan 第二點,約 5000×10000 = 5e7,邊界可行。
#include
using namespace std;
const int MAXN = 5005;
struct Edge { int to, next; } e[MAXN*4];
int head[MAXN], ecnt;
int dfn[MAXN], low[MAXN], dfs_clock;
int cut[MAXN]; // 割點產生的分量數
int del; // 當前忽略的點
void addEdge(int u, int v) {
e[ecnt] = {v, head[u]}; head[u] = ecnt++;
e[ecnt] = {u, head[v]}; head[v] = ecnt++;
}
int dfs(int u, int fa) {
int lowu = dfn[u] = ++dfs_clock;
int child = 0;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (v == del) continue;
if (!dfn[v]) {
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if (lowv >= dfn[u]) cut[u]++;
} else if (dfn[v] < dfn[u]) {
lowu = min(lowu, dfn[v]);
}
}
if (fa == -1 && child == 1) cut[u] = 0;
return lowu;
}
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
// 讀圖、初始化
// 第一次 Tarjan (del=-1) 求 cut[]
// 枚舉 u=1..n, del=u, 第二次 Tarjan 更新答案
// 輸出最大值
}
return 0;
}
| 階段 | 複雜度 |
|---|---|
| 第一次 Tarjan | O(n+m) |
| 枚舉 n 個點 × Tarjan | O(n(n+m)) ≈ 5e7 |
| 總計 | 1-2 秒內 (C++ 優化) |
cut[u] 初始為 0,每滿足 low[v] >= dfn[u] 遞增 1del 變量,DFS 時跳過HDU 4587 考察割點 Tarjan 算法的深度應用:割點分量計數 + 枚舉刪除點重跑 Tarjan。核心難點在於正確計算割點刪除後的連通分量數,並處理根節點特判。此類「刪除 k 個點最大化連通分量」問題,k=1 時單次 Tarjan,k=2 時枚舉+Tarjan,k≥3 通常需更高級結構 (如樹分解)。