hdu3671 Boonie and Clyde Tarjan求割点

Boonie and Clyde

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 773    Accepted Submission(s): 221


Problem Description
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
 

Source
2007 Asia Regional Chengdu
 


思路:http://blog.csdn.net/zjtzyrc/article/details/49075923


#include 
#include 
#include 
#include 
#include 

using namespace std;

int N, M, C;
vector G[1000 + 5];
//rem为删掉的第一个点,index用于给节点打时间戳,cnt存一个连通分支内的节点数量
//ans为割点数目
int rem, index, len, ans, root, num[1000 + 5], cnt[1000 + 5], low[1000 + 5], p[1000 + 5];

int tarjan(int v) {
	low[v] = num[v] = ++index;
	int ret = 1, sons = 0;
	for (int i = 0; i < G[v].size(); i++) {
		int w = G[v][i];
		if (w == rem) {  //排除第一个删除的点
			continue;
		}
		if (!num[w]) {
			p[w] = v;
			sons++;
			ret += tarjan(w);
			if (low[w] >= num[v] && v != root) {
				ans++;
			}
			low[v] = min(low[v], low[w]);
		}
		else if (p[v] != w){
			low[v] = min(low[v], num[w]);
		}
	}
	if (sons >= 2 && v == root) {
		ans++;
	}
	return ret;
}

int main()
{
	C = 0;
	while (~scanf("%d%d", &N, &M) && (N || M)) {
		for (int i = 0; i <= N + 3; i++) {
			G[i].clear();
		}
		for (int i = 0; i < M; i++) {
			int u, v;
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		int res = 0;
		for (int i = 1; i <= N; i++) {  //遍历每一个点
			memset(num, 0, sizeof(num));
			rem = i; index = 0; len = 0; ans = 0;
			for (int j = 1; j <= N; j++) {  //遍历其他非rem的点
				if (j != rem && !num[j]) {  //num[j]为0说明这个连通分支还没算过
					root = j;
					cnt[len++] = tarjan(root);  //len为去掉rem后连通分支数
				}
			}
			if (len >= 3) {  //删掉rem后,原图分为三块或三块以上
				res += N - 1;  //任意找个第二个点都满足
			}
			else if (len == 2) {  //删掉rem后,原图分为两块
				res += N - 1;
				if (cnt[0] == 1) {  //如果其中有一块只有一个节点,情况要减一
					res--;
				}
				if (cnt[1] == 1) {
					res--;
				}
			}
			else if (len == 1) {
				res += ans;  //删掉rem后,原图还是一块(说明删掉的rem不是原图割点,上面两个其实删掉的都是割点)
			}
		}
		printf("Case %d: %d\n", ++C, res / 2);  //res中每种情况算了两边,要除以二
	}
	return 0;
}


你可能感兴趣的:(图论,割点)