http://poj.org/problem?id=3177
又写了一遍手动栈。。。
把边双都缩点,缩成一棵树,答案就是树中度数为1的点的个数除2上取整。
为什么呢?因为1个度数为1的点的树需要多连0条边,2个度数为1的点的树需要多连1条边,3个度数为1的点的树需要多连2条边。
然后对于n(n>3)个度数为1的点的树,连一次边后再缩点能变成有n-2个度数为1的点的树。
无向图边双跟有向图强连通分量的求法很像喔,重点在于特判后继节点是否是自己的father连过来的边。
#include<cstdio>
#include<bitset>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 5003;
const int M = 10003;
bitset <N> inst;
struct node {int nxt, to;} E[M << 1];
int cnt = 1, tot = 0, bel[N], point[N], cur[N], st[N], sta[N], top, statop, fa[N], low[N], dfn[N], n, m, fae[N];
void ins(int u, int v) {E[++cnt] = (node) {point[u], v}; point[u] = cnt;}
void tarjan(int x) {
st[top = 1] = x;
while (top) {
int u = st[top];
if (!dfn[u]) {
inst[sta[++statop] = u] = 1;
dfn[u] = low[u] = ++cnt;
}
if (cur[u]) {
if (cur[u] == fae[u]) {cur[u] = E[cur[u]].nxt; continue;}
int v = E[cur[u]].to;
if (inst[v]) low[u] = min(low[u], dfn[v]);
else if (!dfn[v]) fa[st[++top] = v] = u, fae[v] = cur[u] ^ 1;
cur[u] = E[cur[u]].nxt;
} else {
low[fa[u]] = min(low[fa[u]], low[u]);
if (dfn[u] == low[u]) {
++tot;
while (sta[statop] != u) {
bel[sta[statop]] = tot;
inst[sta[statop]] = 0;
--statop;
}
bel[u] = tot;
inst[u] = 0;
--statop;
}
--top;
}
}
}
int du[N];
int main() {
scanf("%d%d", &n, &m);
int u, v;
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &u, &v);
ins(u, v); ins(v, u);
}
for (int i = 1; i <= n; ++i) cur[i] = point[i];
cnt = 0;
for (int i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= n; ++i)
for (int j = point[i]; j; j = E[j].nxt)
if (bel[i] != bel[E[j].to] && E[j].to > i)
++du[bel[i]], ++du[bel[E[j].to]];
int ret = 0;
for (int i = 1; i <= tot; ++i)
if (du[i] == 1) ++ret;
printf("%d
", (ret + 1) >> 1);
return 0;
}