zoukankan      html  css  js  c++  java
  • HDU 4607 Park Visit (树的最长链)

    Park Visit

    Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 523    Accepted Submission(s): 236


    Problem Description
    Claire and her little friend, ykwd, are travelling in Shevchenko's Park! The park is beautiful - but large, indeed. N feature spots in the park are connected by exactly (N-1) undirected paths, and Claire is too tired to visit all of them. After consideration, she decides to visit only K spots among them. She takes out a map of the park, and luckily, finds that there're entrances at each feature spot! Claire wants to choose an entrance, and find a way of visit to minimize the distance she has to walk. For convenience, we can assume the length of all paths are 1.
    Claire is too tired. Can you help her?
     
    Input
    An integer T(T≤20) will exist in the first line of input, indicating the number of test cases.
    Each test case begins with two integers N and M(1≤N,M≤105), which respectively denotes the number of nodes and queries.
    The following (N-1) lines, each with a pair of integers (u,v), describe the tree edges.
    The following M lines, each with an integer K(1≤K≤N), describe the queries.
    The nodes are labeled from 1 to N.
     
    Output
    For each query, output the minimum walking distance, one per line.
     
    Sample Input
    1 4 2 3 2 1 2 4 2 2 4
     
    Sample Output
    1 4
     
    Source
     
    Recommend
    liuyiding
     

    题意:

    一棵树,问从任意点出发,每次访问k个点走过的最少的边数

    思路:我们可以这样考虑,假设我们走的起点为S,终点为T,那么路径S-T上的点我们必须都走到(因为是一棵树),那么如果路径S-T上的点大于或等于k,则我们只需要沿着路径一直走就行,所以最小长度为k-1,否则,我们一定是在路径上的一些点停下,然后转向其他分支然后再回来,此时才能满足一共走过k个点。假设从S到T的路径上一共有num个点,则最后我们要求的答案ans=num-1+?,这个?即为中间点(可以包括S和T)到别的分支再回来所要走的最小距离,因为每个分支互不影响,所以我们可以对每一个分支分开求,因为图为一棵树,所以我们即是求一棵子树从根节点到他的子节点中遍历x点再回来所要走的最短距离,我们可以有观察加数学归纳法证明这个值为2*x,即从一棵树的根结点往他的子树遍历x个点再回来的最小距离为2*x,所以?=2*(k-num),所以答案ans=num-1+(k-num)*2,要是这个值最小,则要使num最大,所以我们只要求得该树的直径即可,以下时代码,求树的直径是个很经典的问题了,

    算法证明:(转)

    树的直径(Diameter)是指树上的最长简单路。
    直径的求法:两遍BFS (or DFS)
    任选一点u为起点,对树进行BFS遍历,找出离u最远的点v
    以v为起点,再进行BFS遍历,找出离v最远的点w。则v到w的路径长度即为树的直径
    *简单证明
    于是原问题可以在O(E)时间内求出

    关键在于证明第一次遍历的正确性,也就是对于任意点u,距离它最远的点v一定是最长路的一端。
    如果u在最长路上,那么v一定是最长路的一端。可以用反证法:假设v不是最长路的一端,则存在另一点v’使得(u→v’)是最长路的一部分,于是len(u→v’) > len(u→v)。但这与条件“v是距u最远的点”矛盾。
    如果u不在最长路上,则u到其距最远点v的路与最长路一定有一交点c,且(c→v)与最长路的后半段重合(why?),即v一定是最长路的一端

    因为是树是连通的,所以u必有一条路径c和最长路径L相交,len(c)>=1,L被分为两部分,一部分l1,一部分l2
    假设第一次dfs过后,所求最长路径lu端不在L上,那么len(lu)>=len(c)+len(l1)(l1,l2对称,取l1或者l2都一样)
    len(l2+c+lu)>len(l1+l2),矛盾.

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    const int VM=100010;
    
    struct Edge{
        int to,nxt;
    }edge[VM<<1];
    
    int n,m,cnt,head[VM];
    int dis[VM];
    
    void addedge(int cu,int cv){
        edge[cnt].to=cv;    edge[cnt].nxt=head[cu];
        head[cu]=cnt++;
    }
    
    void DFS(int u){
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(dis[v]==-1){
                dis[v]=dis[u]+1;
                DFS(v);
            }
        }
    }
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d%d",&n,&m);
            cnt=0;
            memset(head,-1,sizeof(head));
            int u,v;
            for(int i=1;i<n;i++){
                scanf("%d%d",&u,&v);
                addedge(u,v);
                addedge(v,u);
            }
            memset(dis,-1,sizeof(dis));
            dis[1]=0;
            DFS(1);
            int len=0;
            for(int i=1;i<=n;i++)
                if(dis[i]>len){
                    len=dis[i];
                    u=i;
                }
            memset(dis,-1,sizeof(dis));
            dis[u]=0;
            DFS(u);
            len=0;
            for(int i=1;i<=n;i++)
                if(dis[i]>len)
                    len=dis[i];
            len++;      //加一是因为刚开始的时候那点并不需要走1个距离,加一之后再与k比较
            int k;
            while(m--){
                scanf("%d",&k);
                if(len>=k)
                    printf("%d
    ",k-1);
                else
                    printf("%d
    ",(len-1)+(k-len)*2);
            }
        }
        return 0;
    }
  • 相关阅读:
    iPhone控件之UIDatePicker
    iPhone控件之UIActionSheet
    iPhone控件之UIActivityView
    iPhone控件之UIPickerView2
    RTP/RTCP协议详解
    ASIHTTPRequest详解[转载]
    iPhone控件之UIProgressView
    iPhone控件之UIPageControl
    iPhone控件之UISegmentedControl
    使用AsyncSocket实现RTSP协议
  • 原文地址:https://www.cnblogs.com/jackge/p/3210380.html
Copyright © 2011-2022 走看看