题目大意:给定一棵树,每个结点有一个权值,一棵树的权值为所有结点的权值和,现将这棵树分为两棵子树,要使得两子树的权值差最小。
我的做法是先将无根树化为有根树,然后求每棵子树的权值,最后用一次扫描求结果。需要注意的是结果要用long long型。
View Code
1 #include <stdio.h> 2 #include <string.h> 3 #include <vector> 4 #define N 100000 5 #define MAX(a,b) ((a)>(b)?(a):(b)) 6 #define MIN(a,b) ((a)<(b)?(a):(b)) 7 #define D(a,b) ((a)>(b)?(a)-(b):(b)-(a)) 8 using namespace std; 9 vector<int> g[N],dep[N]; 10 int n,m,a[N],d[N],p[N],dmax; 11 long long sum[N],ans; 12 void make_set() 13 { 14 for(int i=0;i<n;i++) p[i]=i; 15 } 16 int find_set(int i) 17 { 18 return i==p[i]?p[i]:(p[i]=find_set(p[i])); 19 } 20 void dfs(int u,int fa) 21 { 22 int i,v,l; 23 d[u]=(fa==-1?0:d[fa]+1); 24 dmax=MAX(dmax,d[u]); 25 dep[d[u]].push_back(u); 26 l=g[u].size(); 27 for(i=0;i<l;i++) 28 { 29 v=g[u][i]; 30 if(v!=fa) dfs(v,p[v]=u); 31 } 32 } 33 void dp() 34 { 35 int i,j,l,v; 36 memset(sum,0,sizeof(sum)); 37 for(i=dmax;i>=0;i--) 38 { 39 l=dep[i].size(); 40 for(j=0;j<l;j++) 41 { 42 v=dep[i][j]; 43 sum[v]+=a[v]; 44 if(i>0) sum[p[v]]+=sum[v]; 45 } 46 } 47 } 48 int main() 49 { 50 int i,u,v,kase=0; 51 while(scanf("%d%d",&n,&m)&&n) 52 { 53 for(i=0;i<n;i++) scanf("%d",&a[i]); 54 for(i=0;i<n;i++) g[i].clear(); 55 make_set(); 56 for(i=0;i<m;i++) 57 { 58 scanf("%d%d",&u,&v); 59 u--,v--; 60 if(find_set(u)==find_set(v)) continue; 61 p[v]=u; 62 g[u].push_back(v); 63 g[v].push_back(u); 64 } 65 for(i=0;i<n;i++) dep[i].clear(); 66 dmax=0; 67 dfs(0,-1); 68 dp(); 69 ans=sum[0]; 70 for(i=1;i<n;i++) ans=MIN(ans,D(sum[i],sum[0]-sum[i])); 71 printf("Case %d: %lld\n",++kase,ans); 72 } 73 return 0; 74 }