【题意】:给出一棵树,每个节点都有一个权值。问删除一条边之后,分开的两颗子树的权值之差的最少为多少。

【题解】:dfs一次,把无向树变成有向树。
               顺便统计以每个节点为根的子树的权值之和,记为dp[]
               然后我们可以枚举每一条边,由于dfs本身就会把所有边都走一次,所以这个枚举的过程可以直接放在dfs里面。
               那么对于每个非叶子结点u,设v为u的儿子结点。
                        ans = min(ans, abs(sum - 2 * dp[v])), 其中 sum = ∑val[i].

               本题注意要用long long.

【代码】:
 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 #include "algorithm"
 5 #include "vector"
 6 #include "queue"
 7 #include "cmath"
 8 using namespace std;
 9 #define pb push_back
10 #define ll long long
11 #define maxn 100050
12 vector<int> vec[maxn];
13 const ll inf = 100000000000000LL;
14 int n, m;
15 ll val[maxn], dp[maxn], sum, ans;
16 
17 ll ABS(ll x) {
18     return (x > 0) ? x : (-x);
19 }
20 
21 void dfs(int u, int fa) {
22     int size = vec[u].size();
23     dp[u] = val[u];
24     for(int i = 0; i < size; i++) {
25         int v = vec[u][i];
26         if(v == fa) continue;
27         dfs(v, u);
28         dp[u] += dp[v];
29         ans = min(ABS(sum - 2 * dp[v]), ans);
30     }
31 }
32 
33 int main() {
34     int Case = 1, u, v;
35     while(~scanf("%d%d", &n, &m)) {
36         if(!n && !m) break;
37         for(int i = 0; i < maxn; i++) vec[i].clear();
38         ans = inf, sum = 0;
39         for(int i = 1; i <= n; i++) { 
40             scanf("%lld", &val[i]);
41             sum += val[i];
42         }
43         for(int i = 0; i < m; i++) {
44             scanf("%d%d", &u, &v);
45             vec[u].pb(v), vec[v].pb(u);
46         }
47         dfs(1, -1);
48         printf("Case %d: %lld\n", Case++, ans);
49     }
50     return 0;
51 }
52