树的直径 + 强联通分量 -- Warm up HDU - 4612

Warm up HDU - 4612

DCC缩点后建立一棵树, 树中的所有边都是桥。 任意连接一条边使桥的数量最少。 那么就是要找出树的最长路径–树的直径。
ans = 桥的数量 - 树的直径

code:


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 2e5 + 5, M = 1e6 + 5;
struct node{
	int u, v, next;
} g[M * 2];
int dfn[N], low[N], index[N], depth[N], head[N], front[N], stack[N];
int cnt, opt, tot, cot;
bool vis[N], vis_lca[N];

void add(int u, int v){
	g[cnt].u = u;
	g[cnt].v = v;
	g[cnt].next = head[u];
	head[u] = cnt++;
	return;
}

void init(int n){
	cnt = opt = tot = cot = 0;
	for(int i = 1; i <= n; i++){
		dfn[i] = low[i] = 0;
		vis[i] = false;
		index[i] = 0;
		head[i] = front[i] = -1;
	}
	return;
} 

void targan(int u, int in_ridge){
	dfn[u] = low[u] = ++opt;
	vis[u] = true;
	stack[cot++] = u;
	
	for(int i = head[u]; i != -1; i = g[i].next){
		int v = g[i].v;
		if(!dfn[v]){
			targan(v, i);
			low[u] = min(low[u], low[v]);
		}
		else if(vis[v] && i != (in_ridge ^ 1)) low[u] = min(low[u], dfn[v]);
	}
	
	if(dfn[u] == low[u]){
		int res;
		tot++;
		do{
			res = stack[--cot];
			vis[res] = false;
			index[res] = tot;
		} while(u != res);
	}   
	return;
}


int dfs_1(int u, int dep){
	vis_lca[u] = true;
	depth[u] = dep;
	int sno = u;
	int sco;
	for(int i = front[u]; i != -1; i = g[i].next){
		int v = g[i].v;
		if(!vis_lca[v]){
			sco = dfs_1(v, dep + 1);
			if(depth[sno] < depth[sco]) sno = sco;
		} 	
	}
	return sno;
}

int dfs_2(int u, int dep){
	vis_lca[u] = true;
	int sum = dep;
	for(int i = front[u]; i != -1; i = g[i].next){
		int v = g[i].v;
		if(!vis_lca[v]) sum = max(sum, dfs_2(v, dep + 1));
	}
	
	return sum;
}



int main(){
	int n, m, x, y;
	
	while(scanf("%d%d", &n, &m), n && m){
		
		init(n);
		
		for(int i = 1; i <= m; i++){
			scanf("%d%d", &x, &y);
			add(x, y);
			add(y, x);
		}
		
		for(int i = 1; i <= n; i++) if(!dfn[i]) targan(i, -1);
		
		for(int i = 0; i < cnt; i++){
			 int u = g[i].u;
			 int v = g[i].v;
			 if(index[u] != index[v]){
			 	g[i].u = index[u];
			    g[i].v = index[v];
			    g[i].next = front[index[u]];
			    front[index[u]] = i;
			 } 	 
	    } 
		
		for(int i = 1; i <= tot; i++) vis_lca[i] = false;
		int sno = dfs_1(1, 0);
		for(int i = 1; i <= tot; i++) vis_lca[i] = false;
		int sco = dfs_2(sno, 0);
		printf("%d\n", tot - 1 - sco);
		
	} 
	
	
}

dalao的代码,从叶子节点开始,求树的直径和值得借鉴

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2e5 + 5, M = 4e6 + 6;
struct E {int v, next;} e[M];
int n, m, u, v, len, ans, dh[N], h[N], dcc_cnt, id[N], num, dfn[N], low[N], d[N];
bool brid[M], vis[N]; 
void add(int h[], int u, int v) {e[++len].v = v; e[len].next = h[u]; h[u] = len;}
void tarjan(int u, int in_edge) {
	dfn[u] = low[u] = ++num;
	for (int j = h[u]; j; j = e[j].next) {
		int v = e[j].v;
		if (!dfn[v]) {
			tarjan(v, j);
			low[u] = min(low[u], low[v]);
			if (dfn[u] < low[v]) brid[j] = brid[j ^ 1] = true;
		} else if ((j ^ 1) != in_edge) low[u] = min(low[u], dfn[v]);
	} 
}
void dfs(int u) {
	id[u] = dcc_cnt;
	for (int j = h[u]; j; j = e[j].next) {
		int v = e[j].v;
		if (id[v] || brid[j]) continue;
		dfs(v);
	} 
}
void dp(int u) {                                      //倒着求树的直径代码
	vis[u] = true;
	for (int j = dh[u]; j; j = e[j].next) {
		int v = e[j].v;
		if (vis[v]) continue;
		dp(v);
		ans = max(ans, d[u] + d[v] + 1);
		d[u] = max(d[u], d[v] + 1);
	}                                                   //
} 
int main() {
	while (scanf("%d%d", &n, &m), n) {
		memset(h, 0, sizeof(h)); len = num = 1;
		memset(dh, 0, sizeof(dh));
		memset(id, 0, sizeof(id));
		memset(dfn, 0, sizeof(dfn));
		memset(d, 0, sizeof(d));
		memset(brid, false, sizeof(brid));
		memset(vis, false, sizeof(vis));
		for (int i = 1; i <= m; i++) {
			scanf("%d%d", &u, &v);
			add(h, u, v); add(h, v, u);
		}
		tarjan(1, 0); dcc_cnt = 0; 
		//缩点 建树
		for (int i = 1; i <= n; i++) {
			if (!id[i]) {	
			  	dcc_cnt++;
			 	dfs(i);
			 }
		} 
		int tlen = len;  
		for (int j = 2; j <= tlen; j += 2) { 
			u = id[e[j].v], v = id[e[j ^ 1].v];
			if (u == v) continue;
			add(dh, u, v); add(dh, v, u);  
		} 
		ans = 0;
		dp(1); //求出直径 
		printf("%d\n", dcc_cnt - 1 - ans);
	}	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值