题目大意:给定一张图,输出将一条割边去掉后,两个连通块的值得差得最小值,如果没有割边,输出"impossible"
解题:遍历图求割边的同时记录以 u 为起点的未遍历过的路线的值的总和 all[u]. 当遇到割边时就求一次答案,取最小值。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define me(a, x) memset(a, x, sizeof a);
using namespace std;
const int N = 10010, M = 40040;
int n, m;
int h[N], e[M], ne[M], idx;
int low[N], dfn[N], timestamp;
int num[N], sum, ans;
void add(int a, int b)
{
e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++ timestamp;
bool flag = true;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j == fa && flag) {
flag = false;
continue;
}//防止有重边
if(!dfn[j])
{
tarjan(j, u);
num[u] += num[j];
low[u] = min(low[u], low[j]);
if(low[j] > dfn[u]) ans = min(ans, abs(sum - num[j] - num[j]));
}
else low[u] = min(low[u], dfn[j]);
}
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
me(h, -1); me(dfn, 0);
idx = timestamp = sum = 0;
ans = 0x3f3f3f3f;
for(int i = 0; i < n; i ++ )
{
scanf("%d", &num[i]);
sum += num[i];
}
for(int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
tarjan(0, -1);
if(ans != 0x3f3f3f3f) printf("%d\n", ans);
else printf("impossible\n");
}
}