题意:
给出一棵树,每个节点都有价值,问分割那条边能使得分割后的子树的节点价值和的差多小,求出这个最小的值。
题解:
这题说是树形dp,其实可以算是搜索题吧。
定义dp[u]表示以u点为终点的边被割掉能得到的最小差值。sum[u]表示包含u这个点顺着往下子树中节点价值的总和。
那么状态方程就出来了 当u==pre时 dp[u]=sum[u]-(allsum-sum[u]); allsum是所节点价值和。仔细思考这个方程。
注意会爆int用long long可过
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
typedef __int64 lld;
#define oo 0x3f3f3f3f
#define maxn 100005
#define maxm 1000005
lld dp[maxn];
struct EDGE
{
int v,next;
}E[maxm];
lld sum[maxn],val[maxn];
int head[maxn],tol;
lld allsum;
void inst()
{
memset(head,-1,sizeof head);
tol=0;
memset(dp,0,sizeof dp);
}
void add_edge(int u,int v)
{
E[tol].v=v;
E[tol].next=head[u];
head[u]=tol++;
}
lld dfs(int u,int pre)
{
sum[u]=val[u];
for(int i=head[u];i!=-1;i=E[i].next)
{
int v=E[i].v;
if(v==pre) continue;
sum[u]+=dfs(v,u);
}
return sum[u];
}
lld Abs(lld a)
{
return a >= (lld)0 ? a : -a;
}
void tree_dp(int u,int pre)
{
for(int i=head[u];i!=-1;i=E[i].next)
{
int v=E[i].v;
if(v==pre)
dp[u]=Abs(sum[u]-(allsum-sum[u]));
else
tree_dp(v,u);
}
}
int main()
{
int u,v,T,m,n,cas=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
inst();
allsum=(lld)0;
for(int i=1;i<=n;i++)
{
scanf("%I64d",&val[i]);
allsum+=val[i];
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs(1,-1);
tree_dp(1,-1);
lld ans=(lld)1<<60;
for(int i=2;i<=n;i++)
ans=min(ans,dp[i]);
printf("Case %d: %I64d\n",++cas,ans);
}
return 0;
}