【树形DP】POJ 3140 Contestants Division
通道:http://poj.org/problem?id=3140
题意:一棵n个结点的带权无根树,从中删去一条边,使得剩下来的两棵子树的节点权值之和的绝对值最小,并求出得到的最小绝对值
代码:
#include<iostream> #include <cstdio> #include <cstring> #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int MAX = 100005; const int EMAX = 1000005; struct{ int v, nxt; }edge[2*EMAX]; int k, edgeHead[MAX]; int n, m, val[MAX]; bool vis[MAX]; __int64 ans, sum, sumVal[MAX]; void addEdge(int u, int v){ edge[k].v = v; edge[k].nxt = edgeHead[u]; edgeHead[u] = k ++; } void calSum(int u){ // sumVal[u] 记录以u为根的子树的权值之和。 vis[u] = true; sumVal[u] = val[u]; for(int i = edgeHead[u]; i; i = edge[i].nxt){ int v = edge[i].v; if(vis[v]) continue; calSum(v); sumVal[u] += sumVal[v]; } } __int64 myabs(__int64 val){ if(val > 0) return val; else return -val; } void dfs(int u){ vis[u] = true; for(int i = edgeHead[u]; i; i = edge[i].nxt){ int v = edge[i].v; if(vis[v]) continue; dfs(v); ans = min(ans, myabs(sum-sumVal[v]-sumVal[v])); } } int main(){ int u, v, i, t = 1; while(scanf("%d%d", &n, &m) != EOF){ if(n == 0 && m == 0) break; sum = 0, k = 1; for(i = 1; i <= n; i ++){ scanf("%d", &val[i]); sum += val[i]; edgeHead[i] = 0; } while(m --){ scanf("%d%d", &u, &v); addEdge(u, v); addEdge(v, u); } memset(vis, 0, sizeof(vis)); calSum(1); ans = 999999999999999; // ans初始化要足够大,WA了一次。 memset(vis, 0, sizeof(vis)); dfs(1); if(n == 1) ans = val[1]; // n=1时要特殊考虑。 printf("Case %d: %I64d\n", t ++, ans); } return 0; }