[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
#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

转载于:https://www.cnblogs.com/real-l/p/9763553.html

你可能感兴趣的:([10.29 模拟赛] 路由器 (贪心))