題意:給出一張圖,問從中任意去掉兩個點及其鄰接邊,所有可能情況剩下的圖中連通塊的數量最大是多少。
思路:先枚舉去電其中一個點,剩下的一個點用tarjan求無向圖割點類似的方法求個最大值,具體就是當一個點能成爲割點時,我們不是將其標記出來,而是將其計數器+1,最後取一個最大值就行了。
需要注意的就是當根節點爲割點時,將其去掉以後得到的新連通塊數量是son - 1.(son爲搜索樹上其兒子的數量)
這個題逼着我重新理解了一遍tarjan算法求無向圖割邊割點,也算是很有意義了。
代碼:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
const int MAXN = 5010;
int pre[MAXN], cnt;
int dfn[MAXN], low[MAXN], block[MAXN], tid;
int ban;
struct node{
int v, next;
node() {}
node(int _v, int _next) : v(_v), next(_next) {}
}mp[MAXN << 1];
void init()
{
memset(pre, -1, sizeof(pre));
cnt = 0;
}
void add(int u, int v)
{
mp[cnt] = node(v, pre[u]); pre[u] = cnt++;
mp[cnt] = node(u, pre[v]); pre[v] = cnt++;
}
void dfs(int u, int fa)
{
int v, son = 0;
low[u] = dfn[u] = ++tid;
for(int i = pre[u]; ~i; i = mp[i].next)
{
v = mp[i].v;
if(v == fa || v == ban) continue;
if(!dfn[v])
{
son++;
dfs(v, u);
low[u] = min(low[u], low[v]);
if(dfn[u] <= low[v])
block[u]++;
}
else if(dfn[v] < low[u]) low[u] = dfn[v];
}
if(fa == -1)//注意這裏cut[u]並非等於son
block[u] = son - 1;
}
int solve(int n)
{
int ans = 0, tmp;
for(ban = 0; ban < n; ban++)
{
tmp = tid = 0;
memset(block, 0, sizeof(block));
memset(dfn, 0, sizeof(dfn));
for(int i = 0; i < n; i++)
{
if(i == ban || dfn[i]) continue;
dfs(i, -1);
tmp++;
}
for(int i = 0; i < n; i++)
if(i != ban)
ans = max(ans, tmp + block[i]);
}
return ans;
}
int main()
{
int n, m, u, v;
while(~scanf("%d %d", &n, &m))
{
init();
for(int i = 0; i < m; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
}
printf("%d\n", solve(n));
}
return 0;
}