noip模拟赛 兔子

【问题描述】
在一片草原上有N个兔子窝,每个窝里住着一只兔子,有M条路径连接这些窝。更特殊地是,至多只有一个兔子窝有3条或更多的路径与它相连,其它的兔子窝只有1条或2条路径与其相连。换句话讲,这些兔子窝之前的路径构成一张N个点、M条边的无向连通图,而度数大于2的点至多有1个。
兔子们决定把其中K个兔子窝扩建成临时避难所。当危险来临时,每只兔子均会同时前往距离它最近的避难所躲避,路程中花费的时间在数值上等于经过的路径条数。为了在最短的时间内让所有兔子脱离危险,请你安排一种建造避难所的方式,使最后一只到达避难所的兔子所花费的时间尽量少。

【输入】
第一行有3个整数N,M,K,分别表示兔子窝的个数、路径数、计划建造的避难所数。
接下来M行每行三个整数x,y,表示第x个兔子窝和第y个兔子窝之间有一条路径相连。任意两个兔子窝之间至多只有1条路径。

【输出】
一个整数,表示最后一只到达避难所的兔子花费的最短时间。

【输入输出样例1】

rabbit.in 
5 5 2
1 2
2 3
1 4
1 5
4 5

rabbit.out 

1

【输入输出样例1说明】
在第2个和第5个兔子窝建造避难所,这样其它兔子窝的兔子最多只需要经过1条路径就可以到达某个避难所。

【数据规模与约定】
对于30%的数据,N≤15,K≤4;
对于60%的数据,N≤100;
对于100%的数据,1≤K≤N≤1,000,1≤M≤1,500

分析:很明显可以看出这道题要二分,最难的就是要怎么check?假设当前二分的答案是x,我一开始的思路是对于第i个点,将所有与它距离<=x的点打个标记,最后看看能不能有k个点上有所有点的标记.这样就要状压,不好做.换个思路,每个点能覆盖到的点数为2*x+1,处理一下把所有点覆盖需要的点数是不是<=k.可是直接这样处理也非常难,题目中还有一个条件没用上:只有一个度数≥3的点,其它的点的度数≤2,注意≤2这个条件,这说明了一个重要的信息:原图是有一些链和环构成的,都连在一个中心上.

      如果没有环,只有链,就很好做了,设当前链的长度为cnt,那么覆盖这条链所需要的点数位cnt / (2*x + 1)上取整.这样我们在距中心点不超过x的一个点上建造一个避难所,把它覆盖到的点给删掉,这时一定会删掉根节点,那么剩下的就成了若干条链,就很好做了.为什么一定要覆盖中心呢?中心其实就相当于一个“中转站”,覆盖的时候经过这个中转站,剩下的距离每一步都能覆盖多个点,如果不经过中心点的话,每一步就只能覆盖一个点,而且这和之前的二分想法没区别,很难处理.

     二分想不到如何check的时候,把题目中给的所有限制和人为的限制给列出来,如果不满足某种限制的情况很好求,就针对这个限制进行check,例如本题中的k个避难所.

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 3010;

int n, m, k, head[maxn], to[maxn], nextt[maxn], tot = 1, du[maxn], s;
int d[maxn], vis[maxn], ans, cnt;

queue <int> q;

void add(int x, int y)
{
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

void bfs()
{
    q.push(s);
    vis[s] = 1;
    d[s] = 0;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i; i = nextt[i])
        {
            int v = to[i];
            if (!vis[v])
            {
                vis[v] = 1;
                d[v] = d[u] + 1;
                q.push(v);
            }
        }
    }
}

void dfs(int x, int rest)
{
    vis[x] = 1;
    if (!rest)
        return;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
            dfs(v, rest - 1);
    }
}

void dfs2(int x)
{
    cnt++;
    vis[x] = 1;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
            dfs2(v);
    }
}

bool check(int x)
{
    for (int i = 1; i <= n; i++)
    {
        if (d[i] <= x)
        {
            memset(vis, 0, sizeof(vis));
            dfs(i, x);
            int temp = 1;
            for (int j = 1; j <= n; j++)
            {
                if (!vis[j])
                {
                    cnt = 0;
                    dfs2(j);
                    temp += cnt / (x * 2 + 1);
                    if (cnt % (x * 2 + 1))
                        temp++;
                }
            }
            if (temp <= k)
                return true;
        }
    }
    return false;
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        du[x]++;
        du[y]++;
        add(x, y);
        add(y, x);
    }
    s = 1;
    for (int i = 1; i <= n; i++)
        if (du[i] >= 3)
        {
            s = i;
            break;
        }
    bfs(); 
    int l = 0, r = n;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(mid))
        {
            ans = mid;
            r = mid - 1;
        }
        else
            l = mid + 1;
    }
    printf("%d
", ans);

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7732670.html