• 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;
    }
  • 相关阅读:
    Android Studio 快捷键(包含自定义)终极版
    C#开发模式——dll多级引用的问题
    WPF实现QQ群文件列表动画(二)
    WPF实现QQ群文件列表动画(一)
    VS2010Datatable查看器查看超时(Microsoft.VisualStudio.DebuggerVisualizers)
    WPF里ItemsControl的分组实现
    WPF触控程序开发(四)——MultiTouchVista_-_second_release_-_refresh_2的救赎
    WPF异步回调时回调函数如何获取异步函数产生的变量
    裸奔着造房子——对政府禁止采购Win8系统的一些看法
    全球征集-如何实现回文SQL的查询
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7732670.html
Copyright © 2020-2023  润新知