码迷,mamicode.com
首页 > 其他好文 > 详细

2014牡丹江 区域赛 Building Fire Stations

时间:2014-10-13 15:36:09      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:zoj   acm   bfs   2014牡丹间   2014区域赛   

给一棵最多2*10^5个结点的树,选择两个结点放置设备,要求所有结点其到最近设备的最远距离最小,求出这个最小距离。


最大值最小,首先想到二分。二分一个最大距离M,先以1号结点bfs出每个结点的深度。任选一个最大深度的结点,则离他距离M的父结点u上必须要放置一个设备。然后再以u进行bfs,同样的选择出第二个结点。再把选择的两个设备结点加入队列bfs,看是否能够遍历所有点,若能就满足。

zju上面做的,dfs会栈溢出。第一次以一号结点bfs可以预处理,之后直接取出最大深度的点就行。有可能两次找的放置设备的点相同,注意特判一下。二分过程完成后,有可能最后一次判断不成立,此时得到的两个设备的点不是题目所求,需要再判断一次。具体见代码。


//#include <bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//LOOP
#define FF(i, a, b) for(int i = (a); i < (b); ++i)
#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(A,value) memset(A,value,sizeof(A))
//OTHER
#define PB push_back
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
typedef long long LL;
typedef unsigned long long ULL;
typedef vector <int> VI;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-9;
const int MOD = 1000000007;
const double PI = acos(-1.0);
const int maxn = 200010;

int n, M, rt1, rt2, mdrt;
VI G[maxn];
int num[maxn], fa[maxn], pre[maxn];
//指定深度任选的一个点,第一次bfs的父亲结点,第二次bfs父亲结点
bool vis[maxn];

void init()
{
    REP(i, n + 1)   G[i].clear();
}

struct Node{
    int id, d;
    Node() {}
    Node(int a, int b) : id(a), d(b) {}
}t;

int bfs(int s, int ff[])
{
    CLR(vis, 0);
    queue<Node> Q;
    Q.push(Node(s, 0));
    vis[s] = 1, ff[s] = 0;
    int md = 0;
    while (!Q.empty())
    {
        t = Q.front(); Q.pop();
        int u = t.id;
        num[t.d] = u;
        md = max(md, t.d);
        REP(i, G[u].size())
        {
            int v = G[u][i];
            if (!vis[v])
            {
                vis[v] = 1;
                ff[v] = u;
                Q.push(Node(v, t.d + 1));
            }
        }
    }
    return md;
}

int fun()    //标记两个设备能到达的点
{
    CLR(vis, 0);
    int cnt = 0;
    queue<Node> Q;
    vis[rt1] = vis[rt2] = 1;
    Q.push(Node(rt1, 0)), Q.push(Node(rt2, 0));
    while (!Q.empty())
    {
        t = Q.front(); Q.pop();
        if (t.d >= M)   continue;
        int u = t.id;
        REP(i, G[u].size())
        {
            int v = G[u][i];
            if (!vis[v])
            {
                vis[v] = 1;
                Q.push(Node(v, t.d + 1));
            }
        }
    }
    FE(i, 1, n)
        if (vis[i])
            cnt++;
    return cnt;
}

bool ok()
{
    rt1 = mdrt;
    REP(i, M)   rt1 = pre[rt1];
    int md = bfs(rt1, fa);
    rt2 = num[md];
    REP(i, M)   rt2 = fa[rt2];
    int cnt = fun();
    if (cnt >= n)
    {
        if (rt1 == rt2)     //特判两个点相等时
        {
            if (rt1 + 1 <= n)   rt2 = rt1 + 1;
            else        rt2 = rt1 - 1;
        }
        return 1;
    }
    return 0;
}

int main()
{
    int T, x, y;
    RI(T);
    while (T--)
    {
        RI(n);
        init();
        REP(i, n - 1)
        {
            RII(x, y);
            G[x].PB(y), G[y].PB(x);
        }
        int md = bfs(1, pre);
        mdrt = num[md];
        int L = 0, R = md;
        while (L <= R)
        {
            M = (L + R) >> 1;
            if (ok())  R = M - 1;
            else    L = M + 1;
        }
        M = L; ok();  //求得最小距离后再判断一次得出放置设备点
        printf("%d %d %d\n", L, rt1, rt2);
    }
    return 0;
}
/*
8
8
1 2
1 3
2 4
2 5
3 6
5 7
5 8
ans: 2 1 2
*/

2014牡丹江 区域赛 Building Fire Stations

标签:zoj   acm   bfs   2014牡丹间   2014区域赛   

原文地址:http://blog.csdn.net/colin_27/article/details/40043775

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!