这道题最大的一个问题就是如何把无向树(即没有给出父子关系)重新建树,变成有根树,其它人的方法也许好一点,我写的笨了一点
我又重新开了一个vector,用dfs深搜下去重新建树,然后再就是对有根树进行正常的操作了。但是这样浪费了一倍的空间和时间。
其实在原来的无向树上直接深搜也是可以的,只是刚开始没有想到,就写成了重新建树。其实思路是一样的。标记每个节点是否被访问过了,
这样就使得树有了遍历的顺序,就相当于有根了。
最后我们设dp[i]是以i为根的子树所有节点的总人数和。
那么ans=min(ans,abs( (sum-dp[i])-dp[i]) );
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define MAX 100010
int N,M,t=0;
int val[MAX];
vector<int> tree[MAX];
vector<int> retree[MAX];
bool vis[MAX];
__int64 sum,dp[MAX];
__int64 MIN(__int64 a,__int64 b)
{
if(a<b) return a;
return b;
}
__int64 ABS(__int64 a)
{
if(a<0) return -a;
return a;
}
void dfs(int r)
{
int i,j,num;
num=tree[r].size();
for(i=0;i<num;i++)
{
int v=tree[r][i];
if(vis[v]==0)
{
retree[r].push_back(v);
vis[v]=1;
dfs(v);
}
}
}
void DP(int r)
{
int i,j;
int num=retree[r].size();
dp[r]=val[r];
if(num==0)
{
return ;
}
for(i=0;i<num;i++)
{
int v=retree[r][i];
DP(v);
dp[r]+=dp[v];
}
}
void work()
{
int i,j;
t++;
__int64 ans=sum;
for(i=1;i<=N;i++)
{
ans=MIN(ans,ABS((sum-dp[i])-dp[i]));
}
printf("Case %d: %I64d\n",t,ans);
}
int main()
{
int i,j;
int s,t;
while(scanf("%d%d",&N,&M),N)
{
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
for(i=0;i<MAX;i++)
{
tree[i].clear();
retree[i].clear();
}
sum=0;
for(i=1;i<=N;i++)
{scanf("%d",&val[i]);sum+=val[i];}
for(i=1;i<=M;i++)
{
scanf("%d%d",&s,&t);
tree[s].push_back(t);
tree[t].push_back(s);
}
int root=1;
vis[root]=1;
dfs(root);
DP(root);
work();
}
return 0;
}