POJ 3177 Redundant Paths(邊雙連通分量+tarjan)

題目鏈接:http://poj.org/problem?id=3177

題目大意:給你一個連通圖,問你最少添加幾條邊能組成一個邊雙連通圖,有重邊

思路:我們將所有的雙連通塊看成一個點,因爲他們是連通的且不成一個環,利用tarjan縮點後就可以看做一棵樹

這樣的樹至少需要加多少條邊就能構成一個雙連通圖呢,我們只需要將葉子節點連起來即可,因爲是無向圖,所以

度爲1的就是葉節點而不是度爲0,這樣我們要添加的邊數就爲(葉子結點總數+1)/2,加1是因爲葉子結點可能爲

奇數的,而且要注意tarjan在無向圖與有向圖的不同寫法

上代碼:

#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define mod 1e9+7
#define ll long long
#define maxn 5000+10
vector<int>G[maxn];
bool visit[maxn],falg[maxn][maxn];
int dfn[maxn], low[maxn], cnt[maxn];
int n, m,index;
void tarjan(int u, int father)
{
	dfn[u] = low[u] = ++index;
	visit[u] = true;
	for (int i = 0; i < G[u].size(); i++)
	{
		int k = G[u][i];
		if (!visit[k])
		{
			tarjan(k, u);
			low[u] = min(low[u], low[k]);
		}
		else if (visit[k]&&(k != father))//自己不能回到父節點,必須透過其他路徑回去
			low[u] = min(low[u], dfn[k]);//爲什麼是dfn[v],因爲如果low[v]<dfn[v]就說明
		//v已經是別的雙連通分支的點了,不能再去動用他了
	}
}
void solve()
{
	memset(visit, false, sizeof(visit));
	memset(low, 0, sizeof(low));
	memset(dfn, 0, sizeof(dfn));
	memset(cnt, 0, sizeof(cnt));
	index = 0;
	tarjan(1, 1);//如果是不連通的圖,加個for(1 to n)
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j < G[i].size(); j++)
		{
			if (low[G[i][j]] != low[i])
				cnt[low[i]]++;//計算縮點後每個點的度
		}
	}
	int sum = 0;
	for (int i = 1; i <= n; i++)
		if (cnt[i] == 1)//度爲1爲葉節點
			sum++;
	printf("%d\n", (sum + 1) / 2);//爲什麼是(sum+1)/2呢,畫圖好理解
	for (int i = 1; i <= n; i++)
		G[i].clear();
}
int main()
{
	//freopen("Text.txt", "r", stdin);
	int u, v;
	while (scanf("%d%d", &n, &m) != EOF)
	{
		memset(falg, false, sizeof(falg));
		while (m--)
		{
			scanf("%d%d", &u, &v);
			if(!falg[u][v])//去重邊
			{ 
				G[u].push_back(v);
				G[v].push_back(u);
				falg[u][v] = true;
				falg[v][u] = true;
			}
		}
		solve();
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章