[树的直径/贪心] HDU 4607

题意

一棵树,边长1,求走过k个点花费的最小时间

思路

找到树最长的那段路,即树的直径
如果访问的点的个数小于直径上点的个数,则可以直接到,时间就等于点的个数
如果超过直径上点的个数,则需要中途离开直径访问别的店并返回直径,则访问其他店要花费两倍的时间来往返

求直径的方法:
任取一点找到距离他最远的点,作为直径的起点
从起点找到离他最远的点,则是终点,,,
证明…..略………qwq

(是小庆庆的面试题呢!

代码

#include 
#include 
#include 
#include 
#include 

using namespace std;
typedef long long ll;

const int INF = 0x7f7f7f7f;
const int maxn = 1e5 + 10;

int n, m;
vector<int> edg[ maxn ];

int dis[ maxn ];
void dfs ( int now, int last, int len ) {
    dis[ now ] = len; //记录每个点距离开始点的距离
    for ( int i = 0; i < (int)edg[ now ].size (); ++i ) {
        int u = edg[ now ][ i ];
        if ( u != last ) {
            dfs ( u, now, len + 1 );
        }
    }
}

//求直径
int st, ed;
int longestpath () {
    st = 0, ed = 0;
    //任意取一个点,找出距离他最远的点,这个点是直径的一端
    dfs ( 1, 0, 0 );
    for ( int i = 1; i <= n; ++i )
        if ( dis[ st ] < dis[ i ] )
            st = i;

    //从直径的一端开始,离他最远的点是直径的另一端
    dfs ( st, 0, 0 );
    for ( int i = 1; i <= n; ++i )
        if ( dis[ ed ] < dis[ i ] )
            ed = i;
    int mxlen = dis[ ed ];
    return mxlen;
}

int main () {
#ifdef LOCAL
    freopen ( "in", "r", stdin );
    // freopen("out","w",stdout);
#endif
    int t;
    scanf ( "%d", &t );
    while ( t-- ) {
        scanf ( "%d%d", &n, &m );
        //忘记清零,再往里面放入边不但出错而且超内存***
        for ( int i = 0; i <= n; ++i )
            edg[ i ].clear ();

        for ( int i = 0, u, v; i < n - 1; ++i ) {
            scanf ( "%d%d", &u, &v );
            edg[ u ].push_back ( v );
            edg[ v ].push_back ( u );
        }

        int mx = longestpath ();

        while ( m-- ) {
            int k;
            scanf ( "%d", &k );
            if ( k <= mx + 1 )
                printf ( "%d\n", k - 1 );
            else
                printf ( "%d\n", mx + ( k - mx - 1 ) * 2 );
        }
    }

    return 0;
}

你可能感兴趣的:(ACM)