uvalive 5026 树-直径

17 篇文章 0 订阅


这个问题挺有趣,所以锲而不舍想要知道怎么做

了解之后发现也挺直观和朴素,怎么当时就想不出来呢。。

1.求最长路

2.要移动的边肯定在最长路上

3.枚举最长路上的每条边,对于每条边u->v(权值为w),移动它的策略是把u,v两个端点接在两边子树的最长路的中间位置

4.接好后的最长路不一定是 x+w+y,还有可能是两个子树的最长路,要判断下


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 3000
int max(int aa,int bb)
{return aa>bb?aa:bb;}
int min(int aa,int bb)
{return aa<bb?aa:bb;}
struct Edge
{
	int v,w;
	bool flag;
	Edge()
	{
		v=w=0;flag=true;
	}
}edge[N*2];
int head[N],adj[N*2],e,n;
int path[N],dist[N],D,end_v,end_u,temp[N];
void dfs(int u,int fa)
{
	if(D<dist[u])
		D=dist[u],end_v=u;
	for(int i=head[u];i!=-1;i=adj[i])
		if(edge[i].v!=fa&&edge[i].flag)
		{
			dist[edge[i].v]=dist[u]+edge[i].w;
			temp[edge[i].v]=i;
			dfs(edge[i].v,u);
		}
}

void find(int root,int pre_root)
{
	memset(dist,0,sizeof(dist));
	D=-1;
	dfs(root,pre_root);
	memset(dist,0,sizeof(dist));
	D=-1;end_u=end_v;
	dfs(end_u,-1);
}
int subtree_max_mid(int st,int ed,int *t,int DD)
{
	int ret=0x3fffffff;
	int u=ed,j;
	while(u!=st)
	{
		ret=min(ret,max(dist[u],DD-dist[u]));
		j=t[u];
		u=edge[j^1].v;
	}
	if(ret>=0x3fffffff) //没有进入while,没有子树
		return 0;
	return ret;
}

void insert(int u,int v,int w)
{
	edge[e].v=v;edge[e].w=w;
	adj[e]=head[u];head[u]=e++;
}
int main ()
{
	int test;scanf("%d",&test);
	for(int k=1;k<=test;++k)
	{
		memset(head,-1,sizeof(head));
		e=0;
		scanf("%d",&n);
		if(n==1)
		{
			printf("Case %d: 0\n",k);
			continue;
		}
		int u,v,w;
		for(int i=1;i<n;++i)
		{
			scanf("%d%d%d",&u,&v,&w);
			insert(u,v,w);
			insert(v,u,w);
		}
		find(1,-1);
		for(int i=0;i<=n;++i)
			path[i]=temp[i];
		int st,ed,j;
		st=end_u,ed=end_v;
		u=ed;
		int ans=0x3fffffff,res,x,y,d1,d2;
		while(u!=st)
		{
			j=path[u];
			edge[j^1].flag=edge[j].flag=false;
			find(u,edge[j^1].v);
			d1=D;
			x=subtree_max_mid(end_u,end_v,temp,D);
			find(edge[j^1].v,u);
			d2=D;
			y=subtree_max_mid(end_u,end_v,temp,D);
			res=max(d1,d2);
			ans=min(ans,max(res,x+edge[j].w+y));

			u=edge[j^1].v;
			edge[j^1].flag=edge[j].flag=true;
		}
		printf("Case %d: %d\n",k,ans);
	}
	//system("pause");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值