Codeforces Round #527 (Div. 3) E. Minimal Diameter Forest

链接

https://codeforces.com/problemset/problem/1092/E

题解

花了一晚上写这道题(时间都用来调试 p y py py代码了)
对每个连通分量找到一个点,使得这个点到其它点的距离的最大值最小,由于 n ≤ 1000 n\leq1000 n1000可以直接暴力 O ( n 2 ) O(n^2) O(n2)
对于第 i i i个连通分量,记这个点为 c e n t e r i center_i centeri,这个最小的最大距离为 d i d_i di(也就等于直径的一半)
然后找到最大的那个 d i d_i di对应的 i i i,把其余的 c e n t e r j center_j centerj都直接连到 c e n t e r i center_i centeri
为啥这样就最优?
记原来最远的两个点的距离为 D D D
D D D为偶数时:
那么最大的 d i d_i di等于 D 2 D\over 2 2D,如果第二小的 d i d_i di小于 D 2 D\over 2 2D,那么我构造出的树的直径是 D D D,是最优解
如果第二小的 d i d_i di也等于 D 2 D\over2 2D,第三小的 d i d_i di小于 D 2 \frac{D}{2} 2D,我构造出的树直径是 D + 1 D+1 D+1
现在看一下有没有可能存在直径为 D D D的答案呢?
假设这条直径的两个端点为 u , v u,v u,v,假设那棵 d i d_i di也等于 D 2 \frac{D}{2} 2D的树上离根节点距离最远的点是 t t t,那么不管怎么连, u u u肯定最后和 t t t是连通的,显然想要构造尽量小的直径,我肯定直接把 u u u所在的 c o m p o n e n t component component上的某个点直接用一条边连接到 t t t所在的 c o m p o n e n t component component而不是经过多次中转which will lead to bigger answer.那么如果我在 u u u所在的 c o n p o m e n t conpoment conpoment上的点不是距离它恰好 D 2 \frac{D}{2} 2D的点,肯定答案会更劣, t t t所在的分量中的点的选择可以用类似的方法证明,而从一个连通分量到另一个连通分量至少要经过 1 1 1的距离,所以答案不会小于 D + 1 D+1 D+1
如果有三个及以上分量的 d i d_i di都等于 D 2 \frac{D}{2} 2D,那么答案是 D + 2 D+2 D+2
因为有一个分量通过走两条边到达了另一个分量,而因为这是树这样的情况是无法避免的
D D D为奇数时
证明是类似的

温馨提示

p t y h o n ptyhon ptyhon代码调试了一晚上
期间还学会了怎么 t r y , c a t c h try,catch try,catch
最后发现 p y t h o n python python的递归层数的最大值默认是 1000 1000 1000,而这道题恰好有 1000 1000 1000个点
所以就用了这个东西
sys.setrecursionlimit(n)
其中 n n n表示你要把递归层数的上限设为多少

代码

import sys
sys.setrecursionlimit(1100)

def dfs1(u,pre):            #find the components
    vis[u] = True
    now.append(u)
    for v in to[u]:
        if v!=pre:dfs1(v,u)

def dfs2(u,pre):            #calulate the distance
    mxdist[u]=dist[u]
    for v in to[u]:
        if v!=pre:
            dist[v] = dist[u]+1
            dfs2(v,u)
            mxdist[u]=max(mxdist[u],mxdist[v])

try:
    lab = 1
    n, m = [int(x) for x in input().split()]
    to = [[] for i in range(n+10)]
    dist = [0 for i in range(n+10)]
    mxdist = [0 for  i in range(n+10)]

    lab = 2
    for i in range(m):
        u,v = [int(x) for x in input().split()]
        to[u].append(v)
        to[v].append(u)

    com=[]
    vis=[False for i in range(n+10)]
    for i in range(1,n+1):
        if vis[i]==False:
            now=[]
            dfs1(i,0)
            com.append(now)

    lab = 3
    ct = []
    mx = 0
    for lis in com:
        tmp = []
        d = 0
        for root in lis:
            for u in lis:dist[u]=mxdist[u]=0
            dfs2(root,0)
            tmp.append((mxdist[root],root))
            d = max( d, sum( sorted([ mxdist[u] for u in to[root] ])[-2:] ) )
            #print(*[mxdist[u] for u in lis])
        mx = max(mx,d)
        #print('d =',d)
        for x in tmp:
            if x[0]==(d+1)//2:
                center = [x[1] for x in tmp if x[0]==(d+1)//2][0]
        ct.append( ((d+1)//2,center) )

    #print(*ct)

    lab = 4
    ct.sort(reverse=True)
    ans = []
    for i in range(1,len(ct)):
        mx = max(mx,ct[i][0]+1+ct[0][0])
        if i>1:mx = max(mx,ct[i][0]+2+ct[1][0])
        ans.append((ct[i][1],ct[0][1]))
    print(mx)
    for p in ans:
        print(*p)
except Exception as e:
    print('error after lable',lab,', type =',e)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值