[Noi2013]快餐店

Description

小T打算在城市C开设一家外送快餐店。送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方。 快餐店的顾客分布在城市C的N 个建筑中,这N 个建筑通过恰好N 条双向道路连接起来,不存在任何两条道路连接了相同的两个建筑。任意两个建筑之间至少存在一条由双向道路连接而成的路径。小T的快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑的距离不一定是整数)。 现给定城市C的地图(道路分布及其长度),请找出最佳的快餐店选址,输出其与最远的顾客之间的距离。 



Input

第一行包含一个整数N,表示城市C中的建筑和道路数目。
接下来N行,每行3个整数,Ai,Bi,Li(1≤i≤N;Li>0),表示一条道路连接了建筑Ai与Bi,其长度为Li 。

Output

仅包含一个实数,四舍五入保留恰好一位小数,表示最佳快餐店选址距离最远用户的距离。
注意:你的结果必须恰好有一位小数,小数位数不正确不得分。

Sample Input

1 2 1
1 4 2
1 3 2
2 4 1

Sample Output

2.0

题解:

首先可以发现这是一个环套树,要求离最远用户的距离最短,那么这个快餐店一定在某一条路的中点。

两种情况:

1.这条路在环外面,那么答案就是直径>>1

2、这条路经过环,考虑从环上断开一条边,因为ans不可能经过一整个环。所以我们枚举断开的路径,求其最长路径。

断开某一条边的答案可以DP求 
给环上的点标号1~k 

u1[i]树上1~i到1的最短路,u2[i]树上i~k到k的最短路

v1[i]表示1~i的任意两棵树之间组成的最长链的最大值,v2[i]类似,表示的是i~k 

最终ans就为:max(max(v1[i],v2[i+1]),u1[i]+u2[i+1]+(1~k的值))

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
const int N=100005;
struct node{
	int y,z,next;
}sa[N*2];int len=0,first[N];
int n;  
void ins(int x,int y,int z)
{
	len++;
	sa[len].y=y;
	sa[len].z=z;
	sa[len].next=first[x];
	first[x]=len;
}
int fa[N],pos[N],cnt=0,a[N],b[N],c[N],dfn=0;
bool tf[N];
void dfs(int x)
{
	pos[x]=++dfn;
	for(int i=first[x];i!=-1;i=sa[i].next)
	{
		int y=sa[i].y;
		if(fa[x]!=y)
		{
			if(!pos[y])
			{
				fa[y]=x;
				c[y]=sa[i].z;
				dfs(y);
			}
			else if(pos[y]>pos[x])
			{
				for(;y!=x;y=fa[y])
				{
					tf[y]=0;
					a[++cnt]=y;b[cnt]=c[y];
				}
				tf[x]=0;a[++cnt]=x;b[cnt]=sa[i].z;
			}
		}
	}
}
ll f[N];//以i为起点的最长路径
ll ans=0;
void DP(int x,int last)
{
	for(int i=first[x];i!=-1;i=sa[i].next)
	{
		int y=sa[i].y;
		if(tf[y]&&y!=last)
		{
			DP(y,x); 
			ans=max(ans,f[x]+f[y]+sa[i].z);
			f[x]=max(f[x],f[y]+sa[i].z);
		}
	}
}
ll u1[N],u2[N],v1[N],v2[N];
//树上1~i到1的最短路,树上i~k到k的最短路
//表示1~i的任意两棵树之间组成的最长链的最大值,v2[i]类似,表示的是i~k  
int main()
{
	scanf("%d",&n);
	memset(first,-1,sizeof(first));
	memset(tf,1,sizeof(tf));
	int x,y,z;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		ins(x,y,z);ins(y,x,z);
	}
	int i;
	dfs(1);  
    ll sum=0,mx=0;  
    for (i=1; i<=cnt; i++) DP(a[i],0);  
    for (i=1; i<=cnt; i++){  
        sum+=b[i-1]; u1[i]=max(u1[i-1],f[a[i]]+sum);  
        v1[i]=max(v1[i-1],f[a[i]]+sum+mx);  
        mx=max(mx,f[a[i]]-sum);  
    }  
    ll tmp=b[cnt]; sum=mx=b[cnt]=0;  
    for (i=cnt; i; i--){  
        sum+=b[i]; u2[i]=max(u2[i+1],f[a[i]]+sum);  
        v2[i]=max(v2[i+1],f[a[i]]+sum+mx);  
        mx=max(mx,f[a[i]]-sum);  
    }  
    ll mn=v1[cnt];  
    for (i=1; i<cnt; i++) 
	mn=min(mn,max(max(v1[i],v2[i+1]),u1[i]+u2[i+1]+tmp));  
    ans=max(ans,mn);  
    printf("%lld.%d\n",ans>>1,(ans&1)?5:0);  
    return 0;  
}
/*
4
1 2 1

1 4 2

1 3 2

2 4 1*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值