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

题意:一棵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;
}





你可能感兴趣的:(编程,算法,树,ACM,树形DP)