zoukankan      html  css  js  c++  java
  • [10.29 模拟赛] 路由器 (贪心)

    路由器

    题目描述

    Farmer John 最近买了些新电脑,它向为奶牛们提供上网的机会,但是上网需要路由器,FJ想尽量少买路由器。FJ 有N个(编号为1..N)个农场,由 N-1 条长度是1的边连接起来,没有环. FJ 可以决定把路由器放在哪些农场. 每个农场只有长度为 K的网线,该农场可以连接到距离小于等于K的路由器. 路由器本身已经可以连接到互连网,可以被放置到任意的农场.
    FJ至少要买多少个路由器,才能让每个农场都能上网?农场想要上网的话,至少要连接到一个包含路由器的农场.

    多组询问

    输入格式

    第1行:一个整数T,表示数据组数

    第2行:两个整数: N 和 K

    第 3..N行: 每行两个整数ai,bi,表示农场ai与bi之间有长度为1的边.

    输出格式

    共T行:每行一个整数,表示FJ至少需要买多少个路由器.

    输入样例

    1

    8 2

    3 6

    7 1

    1 8

    2 4

    1 4

    4 5

    2 6

    输出样例

    2

    样例解释

    在1号点和2号点安装路由器

    数据范围

    (30\%:n<=100)

    另外(10\%:)保证数据为一条链

    (100\%:n<=100000,k<=10,T<=10)

    保证数据纯随机

    Solution

    30分:枚举

    40 分: 起启示正解的作用 , 我们发现一条链就是一段区间 , 其中的连接情况已经不重要了 , 我们可以从右端点一直往左走 , 每碰到一个没有覆盖的点 , 就在它左方 k 个点放一个路由器 , 然后从这个路由器开始一直向左走 k 个距离 , 都能被覆盖 , 那么其实就是看 n 是否能整除 (2 × k + 1) , 能输出 n/(2 × k + 1) , 否则将答案 +1

    100 分:
    方法一 :
    我们发现,每个农场都要上网,这是解题的关键。我们先搞出一棵树,然后从深度最深的做起。因为最深的也要上网,而且它无法通过其后代的路由器来上网,必然是通过其上方的节点来上网。

    经观察发现,必然是在其祖先上放最好,而且放在能放的最远处最好。因为当前点已经最深,与其共享同一个祖先的点也必然能上网。而越远,放置路由器的上方的点就有更大可能上网。于是我们简单证明了贪心的正确性。

    于是我们按深度排序,将最深的当前不能上网的点向上跳至最远的能使其上网的点,然后将网络信号扩展,将能被影响到的点打上标记。然后继续重复此操作,指导所有点都能上网了为止,同时统计答案。
    这个的时间复杂度呢,如果排序用桶排,仍旧无法保证是线性的,理论上最坏是平方级别的 ( 不可能达到 ) ,所以这就有点玄学了。因为每次打标记后的点还是可能放路由器,很多点会被重复搜到并重复打标记,所以这不是严格的线性了。但实测是能过的,下面有一种更加稳定可靠的方法。

    方法二 :
    直接一遍 (dfs) ,并在 (dfs) 过程中搞贪心。我们记录两个数组 (dis[])(near[].)

    (dis[]) 代表离当前点 (root) 最远的不能上网的后代 ( 包括自己 ) 离当前点的距离是多少 . 一开始(dis[root] = 0 ;)

    (near[]) 代表离当前点 (root) 最近的安装了路由器的后代 ( 包括自己 ) 离当前点 (root) 的距离是多少 .

    一开始 (near[root] = inf .) 我们发现这两个记录的信息很有意思,因为我们知道了最有用的路由器的位置和最需要被 “ 拯救 ” 的节点的位置,由于全部要能上网,所以必须要拯救那个节点。此时就可以贪心了。

    (dfs) 完后代后,先更新出 (near, dis) 的值:

    (near[root] = min(near[root], near[v] + 1);)
    (dis[root] = max(dis[root], dis[v] + 1);)

    然后如果 (dis[root] + near[root] <= K) ,代表不用在此点装路由器就可以使其后代
    ( 包括自己 ) 全部上网,那么 (dis[root] = −1 .)
    如果 (dis[root] == K) ,代表前面更新不了,而且安装路由器 “ 迫在眉睫 ” ,于是答案加1 , (dis[root] = −1, near[root] = 0, ans + +.)
    每个节点都保证其后代都能上网,更新记录的信息,是不是还有点 dp 的意味呢 ?

    最后由于无法保证根节点 1 能上网,要特判一下 ( 就是说根离最远的上不了网的点距离不足 K ,不用急迫的安装,但是往上已经没路了,而自己的一些后代和可怜的自己还无法上网 ) ,这始这需要判断一下 (dis[1]) 是否等于 -1 就行了。

    Code

    #include<bits/stdc++.h>
    #define in(i) (i=read())
    using namespace std;
    
    const int N=1e5+10,inf=2e9;
    
    int read() {
    	int ans=0,f=1; char i=getchar();
    	while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    	while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
    	return ans*=f;
    }
    
    int T,n,k,cur,ans;
    int to[N<<1],nex[N<<1],head[N];
    int dis[N],near[N];
    
    void add(int a,int b) {
        to[++cur]=b,nex[cur]=head[a],head[a]=cur;
        to[++cur]=a,nex[cur]=head[b],head[b]=cur;
    }
    
    void dfs(int u,int fa) {
        dis[u]=0,near[u]=inf;
        for(int i=head[u];i;i=nex[i]) {
            if(to[i]==fa) continue;
            dfs(to[i],u);
            dis[u]=max(dis[u],dis[to[i]]+1);
            near[u]=min(near[u],near[to[i]]+1);
        }
        if(dis[u]+near[u]<=k) dis[u]=-1;
        if(dis[u]==k) dis[u]=-1,near[u]=0,ans++;
    }
    
    int main() {
        freopen("router.in","r",stdin);
        freopen("router.out","w",stdout);
        in(T);
        while(T--) {
            memset(head,0,sizeof(head)); cur=ans=0;
            in(n),in(k);
            for(int i=1,a,b;i<n;i++)
                in(a),in(b),add(a,b);
            dfs(1,0);
            if(dis[1]!=-1) ans++;
            printf("%d
    ",ans);
        }
    }
    
  • 相关阅读:
    iOS 表单 application/x-www-form-urlencoded
    iOS WebRTC
    静态库文件
    .crash 文件解析
    UIWebView转WKWebView,与前端交互的问题
    App Technical Support
    关于URL转义问题
    关于iOS架构相关的博客
    Mac Jenkins
    零碎知识点
  • 原文地址:https://www.cnblogs.com/real-l/p/9763553.html
Copyright © 2011-2022 走看看