POJ 3140 Contestants Division(树形DP)
题意:
给定一棵 n 棵节点的树,求删去某条边后两个分支的最小差异值。
思路:
1. 题目给出来的是一张无向图,所以要设置 vis 标记数组按照深度遍历树的方法来解析,得到 dp[u] : u 为根节点的子树学生数量总数。
2. 最后的到的结果为 ans = min(2 * dp[u] - sum),注意会超过 int 表示范围,用 __int64 范围的数来表示。
3. 学习了一种“孩子链”来表示树的方法,类似于处理 hash 表冲突时采用的“开链法”
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long int
const int MAXN = 100010;
const LL INFS = 0x3fffffff3fffffff;
struct edge {
int v;
edge *next;
} *V[MAXN], ES[MAXN * 2] ;
int EC;
LL dp[MAXN], N[MAXN], sum;
bool vis[MAXN];
inline void addedge(int a, int b)
{
ES[++EC].next = V[a];
V[a] = ES + EC; V[a]->v = b;
}
void treedp(int u)
{
dp[u] = N[u];
vis[u] = true;
for (edge* e = V[u]; e; e = e->next)
{
if (vis[e->v])
continue;
treedp(e->v);
dp[u] += dp[e->v];
}
}
LL solve(int n)
{
LL ans = INFS;
for (int u = 1; u <= n; ++u)
for (edge* e = V[u]; e; e = e->next)
ans = min(ans, _abs64(sum - dp[e->v] * 2));
return ans;
}
int main()
{
int n, m, cc = 0;
while (scanf("%d %d", &n, &m) && n && m)
{
sum = 0, EC = 0;
for (int i = 1; i <= n; ++i)
scanf("%lld", &N[i]), sum += N[i];
memset(dp, 0, sizeof(dp));
memset(V, 0, sizeof(V));
memset(vis, false, sizeof(vis));
for (int i = 0; i < m; ++i)
{
int a, b;
scanf("%d %d", &a, &b);
addedge(a, b);
addedge(b, a);
}
treedp(1);
LL ans = solve(n);
printf("Case %d: %lld\n", ++cc, ans);
}
return 0;
}
-------------------------------------------------------
kedebug
Department of Computer Science and Engineering,
Shanghai Jiao Tong University
E-mail: kedebug0@gmail.com
GitHub: http://github.com/kedebug
-------------------------------------------------------