zoukankan      html  css  js  c++  java
  • [TJOI2017] 城市 (树的直径,贪心)

    题目链接


    Solution

    这道题,调了我一晚上... 一直80分 >_<|| ...
    考虑到几点:

    • 分开任意一条边 (u) ,那么其肯定会断成两棵树.
    • 肯定是分开直径上的边最优,否则原树上最长的边仍然会存在. 其新树直径只有可能更大.
    • 令两棵子树的直径分别为 (dist_1,dist_2) ,选取的两个点分别为 (x_1,x_2.)
      其达到两棵子树的最远距离分别为 (dis_1,dis_2).
      那么组成的新树直径即为:

      [max(dist_1,dist_2,dis_1+dis_2+w_u) ]


    所以我们先枚举断开直径上的边,然后分别找到断开后两棵子树的直径.
    接着我们讨论 (dis_1,dis_2) 最优情况.

    1. (dis) 为其到子树直径较远的一端.
    2. 如果 (x_1,x_2) 在子树的直径上,那么显然会更优,因为如果不在直径上,它还会多出一小段距离.
    3. 然后就可以考虑在直径上的话,显然取直径的中点(如果有的话)会最优,因为此时相当于平分直径,然后使得可能的答案尽量小了.
    4. 如果没有直径中点的话,那么我们可以找到一条“中边”,使得其断开的直径两端距离之差最小.

    那么我们找的策略也就出来了.直接找到两棵子树上直径的 "中边",然后对两条中边上的四个点进行讨论选取即可.


    Code

    #include<bits/stdc++.h>
    #define ll long long
    const ll inf=192608173;
    using namespace std;
    const int maxn=5008;
    struct sj{int to,next;ll w;}
    a[maxn*2];
    int head[maxn],size;
    int v[maxn],now[maxn];
    int road[maxn],road1[maxn];
    int n,num,cntt,cnt,x,y,w;
    ll nowdis,maxx,ans=inf;
    ll xx[maxn],xx1[maxn],dis[maxn];
    ll dis1,dis2,dis3,dis4;
    
    int read()
    {
        int f=1,w=0; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){w=w*10+ch-'0';ch=getchar();}
        return f*w;
    }
    
    void add(int x,int y,int w)
    {
        a[++size].to=y;
        a[size].next=head[x];
        head[x]=size;
        a[size].w=w;
    }
    
    void dfs(int x)
    {
        v[x]=1;now[++num]=x;
        for(int i=head[x];i;i=a[i].next)
        {
            int tt=a[i].to;
            if(!v[tt])
            {
                nowdis+=a[i].w;
                dis[num]=a[i].w;
                dfs(tt);
                nowdis-=a[i].w; 
            }
        }
        if(nowdis>maxx)
        {
            maxx=nowdis; cnt=num;
            for(int i=1;i<=cnt;i++)
            road[i]=now[i],xx[i]=dis[i];
            //每一次 road 都是找出来的临时最长边.
        }
        v[x]=0; num--;return;
    }
    
    int main()
    {
        n=read(); 
        for(int i=1;i<n;i++)
        {
            x=read(); y=read(); w=read();
            add(x,y,w);
            add(y,x,w);
        }
        dfs(1); dfs(road[cnt]); cntt=cnt; 
        for(int i=1;i<=cnt;i++)
        road1[i]=road[i],xx1[i]=xx[i];
        //xx1为原直径上的边长度,road1为原直径上的点.
        for(int i=1;i<cntt;i++)
        {
            ll x1=0,x2=0,maxx1,maxx2;
            dis1=dis2=dis3=dis4=0;
            
            v[road1[i+1]]=1; maxx=-1; 
            //给右边打上标记,让他仅在左边的子树中查询
            dfs(road1[i]); 
            dfs(road[cnt]);
            maxx1=maxx;
            for(int j=1;j<cnt;j++)
            {
                x1+=xx[j];
                if(x1>maxx1-x1)
                {dis1=x1,dis2=maxx1-x1+xx[j];break;}
            }
            //找到"中边"
            
            v[road1[i]]=1; maxx=-1;
            //给左边打上标记
            dfs(road1[i+1]); 
            dfs(road[cnt]); 
            maxx2=maxx;
            for(int j=1;j<cnt;j++)
            {
                x2+=xx[j];
                if(x2>maxx2-x2)
                {dis3=x2,dis4=maxx2-x2+xx[j];break;}
            }
            v[road1[i]]=0;
            
            ans=min(ans,max(dis1+dis3+xx1[i],max(maxx1,maxx2)));
            ans=min(ans,max(dis1+dis4+xx1[i],max(maxx1,maxx2)));
            ans=min(ans,max(dis2+dis3+xx1[i],max(maxx1,maxx2)));
            ans=min(ans,max(dis2+dis4+xx1[i],max(maxx1,maxx2)));	
        }
        cout<<ans<<endl;
    }
    
  • 相关阅读:
    【Redis破障之路】四:Jedis基本使用
    【JVM进阶之路】十二:字节码指令
    Java学习之Spring MVC路由映射
    Java安全之FastJson JdbcRowSetImpl 链分析
    Allatori混淆动态调试分析
    Java工具开发手记
    Java安全之JBoss反序列化漏洞分析
    docker 应用篇————swarm[二十]
    docker 应用篇————docker-compose[十九]
    docker 应用篇————docker 自定义网络[十八]
  • 原文地址:https://www.cnblogs.com/Kv-Stalin/p/9516861.html
Copyright © 2011-2022 走看看