hdu 4118 (树形DP||求树的重心)

来源:互联网 发布:随身带着淘宝到异界 编辑:程序博客网 时间:2024/06/07 10:31

题意:一棵n节点的树,每个节点有一个人,每个人离开自己的位置到另一个位置,每个位置只能有一个人,问这n个人移动距离和的最大值。

思路:11年成都区赛的题目,刚开始想着是匹配问题,但是求出任意两点的距离是不行的,后来想到如果找出树的重心就可以把树分成左右两部分,左边的点跟右边的点交换位置,两点交换位置的距离=两点到根节点的距离之和的2倍,总距离就是所有点到根节点距离之和的2倍了,当时犹豫了一下,如果节点数是奇数的话,这样是不是根节点没换位置呢?后来一想,这种交换位置每个点走的路径都经过根节点,根节点跟任意一个点交换一下就可以了,答案还是一样的。由于没考虑超int的问题wrong了,后来又想到用树形DP可以做,就改了算法。

树形DP:我们可以统计没条边被走了多少次,一条边可以把点分为左右两部分,两部分中点数较少的一部分都要离开自己的位置去另一边,这条边被走了min(son[u](右边点数),son[v])*2次,点数多的一部分有的位置是没变的,但是我们这样把每条边都操作一遍后,所有点的位置都变了。

两种方法的原理都是一样的,都是将树重心左边的点跟右边的点交换,树形DP不用求树的重心。




树形DP:


#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>const int N=100100;int head[N],num,dis[N],son[N],f[N],size,root;__int64 ans;struct edge{int ed,w,next;}e[N*2];void addedge(int x,int y,int w){e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;}int min(int a,int b){if(a<b)return a;return b;}void dfs(int u,int fa){int i,v;son[u]=1;for(i=head[u];i!=-1;i=e[i].next){v=e[i].ed;if(v==fa)continue;dfs(v,u);son[u]+=son[v];__int64 minson=min(son[v],size-son[v]);//边将点分为两部分,求最少的一部分ans+=e[i].w*minson*2;}}int main(){int i,n,t,op=1,x,y,w,sum;scanf("%d",&t);while(t--){memset(head,-1,sizeof(head));sum=0;num=0;scanf("%d",&n);for(i=1;i<n;i++){scanf("%d%d%d",&x,&y,&w);addedge(x,y,w);}ans=0;size=n;dfs(1,0);printf("Case #%d: %I64d\n",op++,ans);}return 0;}


找树的重心然后求点到根节点的距离:


#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>#include<string.h>const int N=100100;int head[N],num,son[N],f[N],size,root;__int64 dis[N],ans;struct edge{int ed,w,next;}e[N*2];void addedge(int x,int y,int w){e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;}int max(int a,int b){if(a>b)return a;return b;}void getroot(int u,int father)//求树的重心  {      int i,v;      f[u]=0;son[u]=1;      for(i=head[u];i!=-1;i=e[i].next)      {          v=e[i].ed;          if(v==father)continue;          getroot(v,u);          son[u]+=son[v];          f[u]=max(f[u],son[v]);      }      f[u]=max(f[u],size-son[u]);      if(f[u]<f[root])root=u;  }  void dfs(int u,int fa){int i,v;for(i=head[u];i!=-1;i=e[i].next){v=e[i].ed;if(v==fa)continue;dis[v]=dis[u]+e[i].w;dfs(v,u);}ans+=dis[u];}int main(){int i,n,t,op=1,x,y,w;scanf("%d",&t);while(t--){memset(head,-1,sizeof(head));ans=0;num=0;scanf("%d",&n);for(i=1;i<n;i++){scanf("%d%d%d",&x,&y,&w);addedge(x,y,w);}root=0;f[root]=size=n;getroot(1,0);dis[root]=0;dfs(root,0);//树的重心为根节点printf("Case #%d: %I64d\n",op++,ans*2);}return 0;}





原创粉丝点击