HDU3671(Boonie and Clyde)

題目傳送門
                     Boonie and Clyde
As two icons of the Great Depression, Bonnie and Clyde represent the ultimate criminal couple. Stories were written, headlines captured, and films were made about the two bank robbers known as Romeo and Juliet in a getaway car.

The new generation of Bonnie and Clyde is no longer cold-blooded killers with guns. Due to the boom of internet, they turn to online banks and scheme to hack the safety system. The safety system consists of a number of computers connected by bidirectional cables. Since time is limited, they decide that they will attack exactly two computers A and B in the network, and as a result, other computers won’t be able to transmit messages via A and B . The attack is considered successful if there are at least two computers (other than A and B ) that disconnected after the attack.

As they want to minimize the risk of being captured, they need to find the easiest way to destroy the safety system. However, a brief study of the network indicates that there are many ways to achieve their objective; therefore they kidnapped the computer expert, you, to help with the calculation. To simplify the problem, you are only asked to tell them how many ways there are to destroy the safety system.

Input
There are multiple test cases in the input file. Each test case starts with two integers N (3<=N<=1000) and M (0<=M<=10000) , followed by M lines describing the connections between the N computers. Each line contains two integers A , B (1<=A, B<=N) , which indicates that computer A and B are connected by a bidirectional cable.

There is a blank line between two successive test cases. A single line with N = 0 and M = 0 indicates the end of input file.

Output
For each test case, output one integer number representing the ways to destroy the safety system in the format as indicated in the sample output.

Sample Input
4 4
1 2
2 3
3 4
4 1

7 9
1 2
1 3
2 3
3 4
3 5
4 5
5 6
5 7
6 7

0 0

Sample Output
Case 1: 2
Case 2: 11

題意

給定一張圖,刪除兩個點後使得至少有兩個以上的連通分量或者說剩下的點不連通,問有多少種方案數可以滿足這個要求。

思路

自己的思路:剛開始想了很久,就是差一點細節沒改出來。剛開始就是分開討論圖的情況

  1. 如果最開始圖就不連通有兩個以上的連通分量那兩個點豈不是隨便刪就可以?
  2. 如果只有一個連通分量的時候枚舉割點,割點刪完掉之後就保證有兩個以上的連通分量,然後第二個點也是可以隨便刪。
  3. 如果是一個點雙連通沒有割點那就開始枚舉刪除一號點,在求割點枚舉刪掉割點。
    上面的做法90%是對的,但是有細節沒有處理就是內部連通塊數目是1的時候沒有做判定,導致wa幾發找不到錯誤。

正確姿勢:直接先枚舉刪除一個點,再跑Tarjan求割點順便計算每個連通分量中有幾個點,和連通分量數目(Tarjan跑的次數)。
4. 如果有至少有3個連通分量,sum += n - 1; //除去刪除的1號點
5. 如果只有一個連通分量,sum += cut[i]。也就是在確定這些點不連接1號點時,刪除一個割點就能滿足條件
6. 如果有2個連通分量,那就需要做連通分量內部點數的分析。如果兩個連通分量內部點數都是1,也就是總共3個點的情況是不滿足條件的sum += 0。如果有一個連通分量是1,一個大於1那麼1那個連通分量不能刪,只能從大於1的那個連通分量裏面刪,sum += n - 2(除去枚舉的1號點和單獨1個點的連通分量),如果兩個連通分量的點數都大於1,也是隨便刪,sum += n-1。

最後別忘了答案要/2,因爲先刪和後刪是同一種情況~

#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <algorithm>
#pragma warning(disable:4996)
using namespace std;
const int maxn = 1005;
vector<int>e[maxn];		
int  dfn[maxn];			
int  low[maxn];			
bool cut[maxn];			
int num,cnt;			//num統計連通分量點數,cnt時間序
int vis,root;			//vis爲枚舉的刪除點,root爲當前搜索樹的根節點
inline void clear_set()
{
	cnt = 0;
	memset(low,0,sizeof(low));
	memset(dfn,0,sizeof(dfn));
	memset(cut,false,sizeof(cut));
}
inline void tarjan(int x,int fx)
{
	dfn[x] = low[x] = ++cnt;
	num++;
	int son = 0;
	for(int i = 0;i < e[x].size();i++){
		int y = e[x][i];
		if(y == fx || y == vis)		continue;			//不能走刪除的一號點和父節點
		if(!dfn[y]){
			son++;
			tarjan(y,x);
			low[x] = min(low[x],low[y]);
			if((x == root && son > 1) || (low[y] >= dfn[x] && x != root)){	//判割點
				cut[x] = true;
			}
		}
		else if(dfn[y] < dfn[x]){
			low[x] = min(low[x],dfn[y]);
		}
	}
}
int main()
{
	int n,m,k = 1;
	while(~scanf("%d%d",&n,&m)){
		if(n == 0 && m == 0)	break;
		for(int i = 0;i <= n+1;i++){
			e[i].clear();
		}
		for(int i = 0;i < m;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			e[x].push_back(y);
			e[y].push_back(x);
		}
		int sum = 0;
		for(vis = 1;vis <= n;vis++){
			clear_set();
			int ans = 0;				//記錄連通分量數目
			int x = -1,y = -1;
			for(int i = 1;i <= n;i++){
				if(vis != i && !dfn[i]){
					root = i;
					ans++;
					num = 0;
					tarjan(i,-1);
					if(x == -1)	x = num;
					else		y = num;
				}
			}
			if(ans == 1){			//只有一個連通分量的情況下
				for(int i = 1;i <= n;i++){
					sum += cut[i];
				}
			}
			else if(ans == 2){		//兩個連通分量
				if(x == 1 && y == 1){		
					continue;
				}
				else if(x == 1 || y == 1){		
					sum += n-2;
				}
				else{
					sum += n-1;
				}
			}
			if(ans > 2){			//連通分量過多,隨便刪
				sum += n-1;
			}
		}
		printf("Case %d: %d\n",k++,sum/2);
	}
	return 0;
}

願你走出半生,歸來仍是少年~

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