百练3383:Cell Phone Network

传送门:http://bailian.openjudge.cn/practice/3383/

【题解】

题目就是最小支配集。

学习了最小支配集的解法:

树形dp(有空可以推一推)

贪心:DFS遍历后逆DFS序进行处理,如果当前这个点不在支配集而且没和支配集连边,那么标记它父亲为支配集成员并处理父亲的父亲和自己(标为和支配集连边的)

具体实现用一个数组记录支配集一个数组记录是否是支配集 or 连过边。

# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static
/*
首先选择一点为树根,再按照深度优先遍历得到遍历序列,按照所得序列
的反向序列的顺序进行贪心,对于一个即不属于支配集也不与支配集中的
点相连的点来说,如果他的父节点不属于支配集,将其父节点加入到支配集
*/

int n;
int head[M], nxt[M], to[M], tot=0;
bool s[M], is[M]; // have been covered yet(in set or connected with ... in set),  in set 

inline void add(int u, int v) {
    ++tot; nxt[tot] = head[u];
    head[u] = tot; to[tot] = v;
}
inline void adde(int u, int v) {
    add(u, v), add(v, u);
}

int dfn[M], DFN=0, fa[M]; 
inline void dfs(int x, int fat) {
    ++DFN; fa[x] = fat; 
    dfn[DFN] = x;
    for (int i=head[x]; i; i=nxt[i]) 
        if(to[i] != fat) dfs(to[i], x); 
}

int main() {
    while(cin >> n) { 
        memset(head, 0, sizeof head); tot = 0;
        DFN = 0; memset(s, 0, sizeof s); memset(is, 0, sizeof is);
        memset(fa, 0, sizeof fa); 
        for (int i=1, u, v; i<n; ++i) {
            scanf("%d%d", &u, &v);
            adde(u, v);  
        }
        dfs(1, 1);
        for (int i=n; i>=1; --i) {
            int x = dfn[i]; 
            if(!s[x]) {
                if(!is[fa[x]]) is[fa[x]] = 1; 
                s[x] = 1; s[fa[x]] = 1; s[fa[fa[x]]] = 1;
            }
        }
        int ans = 0;
        for (int i=1; i<=n; ++i) ans += is[i]; 
        if(n == 1) puts("1");
        else printf("%d\n", ans); 
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/galaxies/p/bailian3383.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值