题意
传送门 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;
}