UVALive - 3902 Network 【树+DFS+贪心】

10 篇文章 0 订阅

题目链接:https://cn.vjudge.net/problem/UVALive-3902

题意:(蓝书)

思路:(把图当作以起始服务器为根的树)

要想满足最少这个条件,就必须尽可能的让叶结点距离服务器远一点,也就是距离正好是 k,这样能够减少很多不必要的服务器;所以可以先把叶结点找出来并且存起来,依次遍历叶结点,以叶结点为准,找一个距离为k的点作为服务器,在把这个服务器k范围内的点都标记为覆盖,覆盖的点就不用考虑了;

为什么一定要从最底层的叶结点开始找服务器?

因为顶部的叶结点可以被下层的服务器覆盖,如果你从顶部的开始找,那么dfs找到的这个服务器可能会在这个叶结点的上层,自然就不会是最优解,但是如果是从底层开始,那么只可能找到比底层叶结点高的点作为服务器,这样就能保证最优解了;

这里要注意的是,遍历的点都是k+1层到n-1的点,因为我们根本不用考虑k层内的点,起始点就已经覆盖k层内的点了;下面的代码基本上和蓝书的一样,有很多值得学习的地方,我都用注释标记出来了;

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<sstream>
#include<vector>
#include<string>
#include<set>

using namespace std;

#define IOS ios::sync_with_stdio(false); cin.tie(0);
#define REP(i,n) for(int i=0;i<n;++i)

int read(){

    int r=0,f=1;char p=getchar();
    while(p>'9'||p<'0'){if(p=='-')f=-1;p=getchar();}
    while(p>='0'&&p<='9'){r=r*10+p-48;p=getchar();}return r*f;
}

typedef long long ll;
typedef unsigned long long ull;
const int Maxn = 1000+10;
const long long LINF = 1e18;
const int INF = 0x3f3f3f3f;
const int Mod = 10001;
const double PI = acos(-1.0);

vector<int> edges[Maxn],nodes[Maxn];
int fa[Maxn],k,n,s;
bool used[Maxn];

void dfs (int u,int f,int d) { // 把所有 深度大于k的叶结点存入nodes中,并且完成fa[]的赋值
    fa[u] = f;                  // fa[]能存储所有点的父节点,树的每一个节点只有一个前继
    int m = edges[u].size();
    if(m == 1 && d > k) nodes[d].push_back(u); // 这里的nodes是用来存储第d层的所有叶节点
    for (int i = 0; i < m; ++i) {
        int v = edges[u][i];
        if(v != f) dfs (v,u,d+1); // 这里的f是为了避免重复走之前走过的路
    }
}

void dfs2 (int u,int f,int d) {
    used[u] = true;
    int m = edges[u].size();
    for (int i = 0; i < m; ++i) {
        int v = edges[u][i];
        if(v != f && d < k) dfs2 (v,u,d+1); // f的作用就是为了避免重复走之前走过的路,
    }                                       //不用多开一个vis的数组;d < k : 把从服务器开始的
}                                              //的k范围内的点全部覆盖

int solve () {
    memset(used,0,sizeof(used));
    int v,ans = 0;
    for (int d = n-1; d > k; --d) { // 深度从k+1到n-1
        for (int j = 0; j < nodes[d].size(); ++j) {
            v = nodes[d][j];
            if(used[v]) continue; // 处理过的点就直接跳过

            for (int i = 0; i < k; ++i) v = fa[v];  // 从叶结点开始的第k个中间节点作为服务器
            dfs2 (v,-1,0);                      //用fa不断的向上找前继(fa在找叶结点的时候就顺便完成赋值了)
            ans++;
        }
    }
    return ans;
}

int main (void)
{
    int t;
    scanf("%d",&t);
    while (t--) {
        scanf("%d%d%d",&n,&s,&k);
        for (int i = 0; i <= n; ++i) { edges[i].clear(); nodes[i].clear(); }
        int u,v;
        for (int i = 1; i < n; ++i) { // n个点,n-1条边,一棵树
            scanf("%d%d",&u,&v);
            edges[u].push_back(v);
            edges[v].push_back(u);
        }
        dfs (s,-1,0);
        printf("%d\n",solve ());
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值