双连通分量缩点后重新构图,答案为叶子节点个数除以二,向上取整
证明:
显然每个叶子节点至少至少需要连一条边出去,否则该节点被孤立,答案>=叶子节点个数/2,向上取整;
对于每一个叶子节点,寻找与他距离最远的一个叶子节点连边,反复操作直到没有孤立的叶子节点,这显然是一组合法解,故等号成立
证毕
#include <iostream>
#include <cstdio>
#include <stack>
#include <cstring>
#define N 200050
using namespace std;
int dfn[N],low[N],ins[N],h[N],g[N],rd[N],tot,cnt;
bool con[5050][5050];
int n,m;
struct Edge{ int a,b,next; }e[10*N];
void add(int x,int y) {
e[++cnt].a = x;
e[cnt].b = y;
e[cnt].next = h[x];
h[x] = cnt;
}
stack<int> sta;
int tme;
void Tarjan(int u,int fa) {
dfn[u] = low[u] = ++tme;
sta.push(u); ins[u] = 1;
for (int i=h[u];i;i=e[i].next) {
int v = e[i].b; if (v == fa) continue;
if (!dfn[v]) {
Tarjan(v,u);
low[u] = min(low[u] , low[v]);
} else {
low[u] = min(low[u] , dfn[v]);
}
}
if (low[u] == dfn[u]) {
++tot;
do {
int x = sta.top(); sta.pop();
ins[x] = 0; g[x] = tot;
} while (ins[u]);
}
return ;
}
int main() {
while (~scanf("%d%d",&n,&m)) {
memset(con,0,sizeof(con));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(h,0,sizeof(h));
memset(e,0,sizeof(e));
memset(rd,0,sizeof(rd));
memset(g,0,sizeof(g));
memset(ins,0,sizeof(ins));
tme = cnt = tot = 0;
for (int _=1;_<=m;_++) {
int x,y; scanf("%d%d",&x,&y);
if (con[x][y]) continue;
add(x,y); add(y,x);
con[x][y] = con[y][x] = 1;
}
Tarjan(1,0);
for (int _=1;_<=cnt;_+=2) {
int u = e[_].a , v = e[_].b;
if (g[u] != g[v]) rd[ g[u] ]++ , rd[ g[v] ]++;
}
int ans = 0;
for (int i=1;i<=tot;i++) if (rd[i] == 1) ans++;
printf("%d\n",(ans+1)/2);
}
return 0;
}