POJ 3713 枚举 + Tarjan 割点

题意

传送门 POJ 3713

题解

白书里归到最大流最小割,Emmmm没有找到复杂度比较低的方法。虽然通道节点不相交可以转化为节点容量为 1,通过拆成 2 个节点并连边转化成最大流问题,但要枚举每一对节点。决定换个思路。

可以通过枚举删除每一个节点,通过 Tarjan 算法判断是否出现割点,或是否有一次 dfs 无法遍历到的点。若出现割点,或遍历不到的点,都不满足题设的三连通条件。

#include <cstdio>
#include <STDLIB.H>
#include <algorithm>
#include <vector>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-3
#define M_PI 3.14159265358979323846
#define MAX_N 500
using namespace std;
int N, M;
vector<int> G[MAX_N];

bool cut; //是否存在割点
int del, root, dfs_clk; //删除节点, 根节点, dfs 时间戳
int dfn[MAX_N], low[MAX_N]; // dfs 搜索到某节点时的时间戳, 从某节点或其子孙节点可回溯节点的最小时间戳
void Tarjan(int u, int p){
	if(cut) return;
	int son = 0; //子树数量
	dfn[u] = low[u] = ++dfs_clk;
	for(int i = 0; i < G[u].size(); i++){
		int v = G[u][i];
		if(v == p || v == del) continue; //跳过删除节点及父节点
		if(!dfn[v]){
			++son;
			Tarjan(v, u);
			low[u] = min(low[u], low[v]);
			if((u == root && son > 1) || (u != root && dfn[u] <= low[v])){
				cut = 1;
				return;
			}
		}
		else low[u] = min(low[u], dfn[v]);
	}
}

int main(){
	while(~scanf("%d%d", &N, &M) && (N || M)){
		for(int i = 0, a, b; i < M; i++){
			scanf("%d%d", &a, &b);
			G[a].push_back(b);
			G[b].push_back(a);
		}
		cut = 0;
		for(int i = 0; i < N; i++){ 
			//枚举删除的点, 且根节点不与删除节点重复
			del = i, root = !i, dfs_clk = 0;
			// dfn[u] 为 0 代表未搜索到节点 u
			memset(dfn, 0, sizeof(dfn));
			Tarjan(root, -1);
			if(cut) break;
			dfn[del] = 1;
			for(int j = 0; j < N; j++){
				if(!dfn[j]){
					cut = 1;
					break;
				}
			}
			if(cut) break;
		}
		printf("%s\n", cut ? "NO" : "YES");
		//清空邻接表
		for(int i = 0; i < N; i++) G[i].clear();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值