• 「日常训练&知识学习」树的直径(POJ-1849,Two)


    题意

    一个城市由节点和连接节点的街道组成,街道是双向的。
    此刻大雪覆盖了这个城市,市长确定了一些街道要将它们清扫干净,这些街道保证所有的节点可以通过它们连通而且街道数目尽可能小。
    现有两台相同的扫雪机(S)(M),它们的起点在同一个节点上。
    所有被确定的街道必须至少被一台扫雪机经过,才能完成清扫任务,完成任务后(S)(M)可以在原地停下,不必集合到某一点。
    扫雪机的行进是需要耗费油量的(即使扫雪机行驶的是已被扫净的街道),因此扫雪机行进的总距离越小越好,你需要计算两台扫雪机完成任务的最小总行进距离。

    分析

    题目的条件暗示了这是一棵树,问题来了,题意的最短距离(两个扫雪机的情况下)意味着什么?
    我们先考虑一个扫雪机的情况。这样,从任意一个地方开始,然后转化为以其为根的有根树,遍历它的所有子树,完事了。最后一个“最长”的子树不用回来,这样在最优的情况下走多久呢?2*所有边长-全图的最长边——也就是图的直径,这是它的定义。
    那么两个机子意味着什么呢?很意外,其实还是这个答案,哈哈。为什么?因为既然转化为以直径的端点为根(见上最优的情况)的有根树了,那么这种情况下根结点必只有一个子节点。那么无论怎么走,他们仅仅只能省去走直径的“回程”,在子树里他们是无论如何要走一遍回来的,这样,与一个扫雪机的情况是一致的。
    参见:https://www.cnblogs.com/dilthey/p/7231438.html
    接下来解决树的直径问题——树的最长边。
    做法是这样的:
    从随便一个点(记为(w))出发,到一个节点结束,记这个节点为(u);然后从这个点出发,到另一个点完事,记这个节点为(v),那么长度就是u到v的长度。
    参见:https://blog.csdn.net/tc_to_top/article/details/47002255
    这样为什么能work呢?

    1. 如果(w)是直径上的一点,那么u一定是直径的一个端点,否则从另一端点出发到达(u)反而比直径长了,不合题意。
    2. 如果(w)不是的话,那它到最远的点必经过一直径上的点(记为(c)),后同(1)的情况。为什么?如果不经过直径的一点,此时有(dis[w,u']=dis[w,x]+dis[x,u']>dis[w,t]+dis[t,u]),其中(t)是直径上的一点,(x)(w)(t)的一点。那么(dis[x,u']>dis[x,t]+dis[t,u]),也就有(dis[v,t]+dis[t,x]+dis[x,u']=dis[v,u']>dis[v,u]=dis[v,t]+dis[t,u]),这就坏事了:我们明明有((v,u))是树的直径的。所以得到矛盾,原先的证明不成立。

    参见:https://blog.csdn.net/qq_32400847/article/details/51469917

    代码

    /*
     * Filename: poj1849.cpp
     * Date: 2018-11-03
     */
    
    #include <cstring>
    #include <vector>
    #include <iostream>
    
    #define INF 0x3f3f3f3f
    #define PB push_back
    #define MP make_pair
    #define fi first
    #define se second
    #define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
    #define per(i,a,b) for(repType i=(a); i>=(b); --i)
    #define ZERO(x) memset(x, 0, sizeof(x))
    #define MS(x,y) memset(x, y, sizeof(x))
    #define ALL(x) (x).begin(), (x).end()
    
    #define QUICKIO                  
        ios::sync_with_stdio(false); 
        cin.tie(0);                  
        cout.tie(0);
    #define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
    
    using namespace std;
    typedef int repType;
    typedef long long ll;
    
    int n,s;
    const int MAXN=100005;
    struct Edge
    {
        int u,v,w;
        Edge(int _u, int _v, int _w):
            u(_u), v(_v), w(_w) {}
    };
    vector<Edge> edges;
    vector<int> G[MAXN];
    
    void add_edge(int u, int v, int w)
    {
        edges.PB(Edge(u,v,w));
        G[u].PB(int(edges.size())-1);
    }
    
    int dist[MAXN];
    void dfs(int x)
    {
        rep(i,0,int(G[x].size())-1)
        {
            if(dist[edges[G[x][i]].v]==-1)
            {
                dist[edges[G[x][i]].v]=dist[x]+edges[G[x][i]].w;
                dfs(edges[G[x][i]].v);
            }
        }
    }
    
    int
    main()
    {
    QUICKIO
        while(cin>>n>>s)
        {
            edges.clear();
            rep(i,1,n) G[i].clear();
            ll ans=0;
            rep(i,1,n-1)
            {
                int a,b,c;
                cin>>a>>b>>c;
                add_edge(a,b,c);
                add_edge(b,a,c);
                ans+=c;
            }
            MS(dist, -1);
            dist[s]=0;
            dfs(s);
            int maxi=s;
            rep(i,1,n) if(dist[maxi]<dist[i]) maxi=i;
            MS(dist, -1);
            dist[maxi]=0;
            dfs(maxi);
            rep(i,1,n)
            {
                if(dist[maxi]<dist[i]) maxi=i;
            }
            cout<<2*ans-dist[maxi]<<endl;
        }
        return 0;
    }
    
    如非注明,原创内容遵循GFDLv1.3发布;其中的代码遵循GPLv3发布。
  • 相关阅读:
    [Sqlite] 移动嵌入式数据库Sqlite日报SQL操作语句汇总
    Matlab spline
    读书笔记:《重来REWORK》
    读书笔记:《一生的计划》
    6 款好用的 PC+Android 同步 GTD 软件
    第35本:《像外行一样思考,像专家一样实践》
    第34本:《暗时间》
    第33本:《删除:大数据取舍之道》
    第32本:《超级时间整理术----每天多出一小时》
    第31本: 思考的乐趣
  • 原文地址:https://www.cnblogs.com/samhx/p/POJ-1849.html
Copyright © 2020-2023  润新知