Live Archive 3902 Network 树 贪心

题意:给出小于等于1000个结点的树,叶子结点为客户端,其他结点为服务器。给出点S,代表只有服务器S安装了某项服务,给出距离K,代表每个客户端在不超过K的距离范围内必须有一个安装了该服务的服务器。问至少还有多少服务器需要安装该服务?


解法:之前想的是DP,看了解法感觉想麻烦了,因为了没有分析性质。不过有一点想对了,因为子树之间互相关联,不能考虑某个服务器安装或者不安装服务。考虑的落脚点是每个客户端依赖于哪个服务器。


将无根树转化为以S为根的有根树,对于每个叶子结点,当然是选择越靠近根结点的服务器作为其最近依赖点最好(贪心)。



#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 1000   ;
int n,S,K;
vector<int >G[maxn+10],nodes[maxn+10];
int pre[maxn+10];
bool covered[maxn+10];
void init()
{
    memset(covered,0,(n+1)*sizeof covered[0]);
    for1(i,n)  G[i].clear(),nodes[i].clear();
}

void dfs(int x,int fa,int d)
{
    if(fa!=-1)  pre[x]=fa;
    int siz=G[x].size();
    if(siz==1)
    {
        nodes[d].push_back(x);
        if(d<=K) covered[x]=1;
    }

    for(int i=0;i<siz;i++)
    {
        int y=G[x][i];
        if(y==fa) continue;
        dfs(y,x,d+1);
    }


}



void dfs2(int x,int fa,int d)
{
    if(d<=K)  covered[x]=1;

    int siz=G[x].size();
    for0(i,siz)
    {
        int y=G[x][i];
        if(y==fa) continue;
        if(d<K)  dfs2(y,x,d+1);
    }
}
int solve()
{
    int ans=0;
   for(int d=n-1;d>K;d--)
   {
       int siz=nodes[d].size();
       for0(i,siz)
       {
           int y=nodes[d][i];
           if(covered[y])  continue;

           int t=y;
           for1(i,K) t=pre[t];//覆盖距离为K,之前写成了K-1
           dfs2(t,-1,0);
           ans++;
       }
   }
   return ans;
}
int main()
{
   int T; scanf("%d",&T);
   while(T--)
   {
       scanf("%d%d%d",&n,&S,&K);
       int x,y;
       init();
       for1(i,n-1)
       {
           scanf("%d%d",&x,&y);
           G[x].push_back(y);
           G[y].push_back(x);
       }
       pre[S]=S;
       dfs(S,-1,0);
       printf("%d\n",solve() );
   }
   return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值