hdu 3721 uvalive 5026 building roads

首先,我们证明一个结论:树的重心一定在树的直径上

树的直径指树上最长的一条路径,树的重心指树上所有点中到其余点最远距离最小的点

假设重心u不在直径上,那么它到距它最远点(x)的路径一定会和树的直径有交点v,否则这条路径会是新的直径的一部分,那么v到x的距离一定更小,所以重心一定在直径上


显然,我们需要移走直径上的一条边,并且移走这条边后,原来的树变成了两棵树(特殊情况是移走和叶子相连的一条边,但不影响)

那这样子,这两棵树,每棵树都有自己的直径d1,d2,那么将两棵树重新合并后,当前最优解tmp>=max(d1,d2)

我们在树1中和树2中分别找重心,新合并的树上任意点到最远点距离的最小值也可能是是树1中的重心的相对应的值x+树2中的重心相对应的值y+枚举的边长w

即tmp=max(tmp,w+x+y)


代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAX=2555;
struct node 
{
	int v,w,next,flag;
}g[MAX*10];
int adj[MAX],e,n,m,dis[MAX],pre[MAX],D,tot,tmp[MAX],res[MAX];
void add(int u,int v,int w)
{
	g[e].v=v; g[e].w=w; g[e].flag=1; g[e].next=adj[u]; adj[u]=e++;
}
void dfs(int u,int fa,int w,int id)
{
	int i,v;
	pre[u]=id;
	dis[u]=w;
	D=max(D,w);
	for(i=adj[u];i!=-1;i=g[i].next)
	{
		v=g[i].v;
		if(v==fa||!g[i].flag)
			continue;
		dfs(v,u,w+g[i].w,i);
	}
}
void find(int root)
{
	memset(dis,0,sizeof(dis));
	dfs(root,-1,0,-1);
	int ma=-1,i,j;
	for(i=1;i<=n;i++)
		if(dis[i]>ma)
		{
			ma=dis[i];
			j=i;
		}
	memset(dis,0,sizeof(dis));
	D=-1;
	dfs(j,-1,0,-1);
}	
void find_path(int t,int res[],int &tot)
{
	int i;
	tot=0;
	for(i=pre[t];i!=-1;i=pre[g[i^1].v])
	{
		res[tot++]=i;
	}
}	
void solve()
{
	int ans,i,j,x,y,k;
	find(1);
	ans=1<<30;
	for(i=1;i<=n;i++)
	{
		if(dis[i]==D)
		{
			find_path(i,res,tot);
			break;
		}
	}
	//cout<<"tot="<


你可能感兴趣的:(树形DP)