zoukankan      html  css  js  c++  java
  • CH Round #72树洞[二分答案 DFS&&BFS]

    树洞 CH Round #72 - NOIP夏季划水赛

    描述

    在一片栖息地上有N棵树,每棵树下住着一只兔子,有M条路径连接这些树。更特殊地是,只有一棵树有3条或更多的路径与它相连,其它的树只有1条或2条路径与其相连。换句话讲,这些树和树之间的路径构成一张N个点、M条边的无向连通图,而度数大于2的点至多有1个。
    近年以来,栖息地频繁收到人类的侵扰。兔子们联合起来召开了一场会议,决定在其中K棵树上建造树洞。当危险来临时,每只兔子均会同时前往距离它最近的树洞躲避,路程中花费的时间在数值上等于距离。为了在最短的时间内让所有兔子脱离危险,请你安排一种建造树洞的方式,使最后一只到达树洞的兔子所花费的时间尽量少。

    输入格式

    第一行有3个整数N,M,K,分别表示树(兔子)的个数、路径数、计划建造的树洞数。
    接下来M行每行三个整数x,y,表示第x棵树和第y棵树之间有一条路径相连。1<=x,y<=N,x≠y,任意两棵树之间至多只有1条路径。

    输出格式

    一个整数,表示在最优方案下,最后一只到达树洞的兔子所花费的时间。

    样例输入

    5 5 2
    1 2
    2 3
    3 1
    1 4
    4 5

    样例输出

    1

    数据范围与约定

    • 对于20%的数据,1 ≤  n ≤ 10。
    • 对于另外30%的数据,每棵树至多与2条路径相连。
    • 对于另外30%的数据,保证存在一种最优解,使与3条或更多路径相连的树上一定建造了树洞。
    • 对于100%的数据,1 ≤ n ≤ 2000,n-1<=m<=n*(n-1)/2。​

    一开始读错题

    官方题解:

    二分答案。
    枚举距离特殊点最近的建造的树洞是哪一个,记为X。
    在图中删除能够在二分的时间内到达该树洞X的所有点。
    此时图变为若干条独立的链,直接求最少需要的树洞数。
    在所有枚举的情况中取最小值,与K比较确定二分范围变化。

    其实这就是一条链,只不过有一个点可能连出多条链或者连乘环

    一条链的话,答案就是(n-t)/(2*t)上取整

    很明显可以二分最大时间,也就是节点的最短距离

    把特殊点设为root,枚举从能覆盖root的点中选一个建造树洞,

    剩下的没vis的全成了链,每条链每2*mid+1必定放一个树洞

    注意cnt要+1,一开始建了一个嘛

    //
    //  main.cpp
    //  ch72c
    //
    //  Created by Candy on 26/10/2016.
    //  Copyright © 2016 Candy. All rights reserved.
    //
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=2010,INF=1e9;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    int n,m,t,x,y;
    struct edge{
        int v,ne;
    }e[N<<1];
    int h[N],cnt=0;
    void ins(int u,int v){
        cnt++;
        e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
        cnt++;
        e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
    }
    int root=0,de[N];
    int d[N],q[N],head=1,tail=0;
    void bfs(int s){
        memset(d,-1,sizeof(d));
        head=1;tail=0;
        q[++tail]=s;
        d[s]=0;
        while(head<=tail){
            int u=q[head++];
            for(int i=h[u];i;i=e[i].ne){
                int v=e[i].v;
                if(d[v]==-1){
                    d[v]=d[u]+1;
                    q[++tail]=v;
                }
            }
        }
    }
    int vis[N],len;
    void dfs(int u){
        vis[u]=1;len++;
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v;
            if(vis[v]) continue;
            dfs(v);
        }
    }
    int solve(int dis){
        int ans=INF;
        for(int x=1;x<=n;x++){
            bfs(x);
            int cnt=0;
            if(d[root]>dis) continue;
            memset(vis,0,sizeof(vis));
            for(int i=1;i<=n;i++) if(d[i]<=dis) vis[i]=1;
            for(int i=1;i<=n;i++) if(!vis[i]){
                len=0;
                dfs(i);
                cnt+=(len-1)/(2*dis+1)+1;
            }
            ans=min(ans,cnt+1);//!!!cnt+1
        }
        return ans;
    }
    int main(int argc, const char * argv[]) {
        n=read();m=read();t=read();
        for(int i=1;i<=m;i++){x=read();y=read();de[x]++;de[y]++;ins(x,y);}
        for(int i=1;i<=n;i++) if(de[i]>3) {root=i;break;}
        if(!root){
            printf("%d",(n-t-1)/(2*t)+1);
            return 0;
        }
        if(n==t){printf("0");return 0;}
        int l=1,r=n,ans=INF;
        while(l<=r){
            int mid=(l+r)/2;
            if(solve(mid)<=t) ans=min(ans,mid),r=mid-1;
            else l=mid+1;
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    《自己动手写操作系统》:开发环境配置心得
    sip.conf配置详情
    MySQL字符串中数字排序的问题
    Asterisk iax错误提示
    Python 快速入门
    C# winfrom 导出word
    SetWindowsHookEx函数参数详解
    Ubuntu Linux系统下轻松架设nginx+php服务器应用
    TShockwaveFlash的使用
    检讨
  • 原文地址:https://www.cnblogs.com/candy99/p/6003565.html
Copyright © 2011-2022 走看看