[BZOJ1596]-[Usaco2008 Jan]电话网络-贪心

说在前面

并没有什么想说的,但是要保持格式=w=


题目

BZOJ1596传送门

题目大意

给出一棵 n 个节点的树,求该树的最小支配集
一个点可以支配与它相临的点
支配集:选出一个集合,满足所有点都被支配
范围: n 10000

输入输出格式

输入格式:
一棵树的常识输入

输出格式:
输出一个整数表示答案


解法

树形dp是可以的,而且很显然
要不是有这个贪心me也不会写这一篇

注意到,如果我们把子树内的选择方案确定了,那么向上的最优方案是可以构造的
因为我们要尽可能多的覆盖点,假设子树已经被覆盖,那么当 儿子/自己/父亲 都没有被选择的时候,选择父亲,这样就能尽可能多的覆盖住所有点

那么边界情况是什么呢?因为要覆盖住叶子节点,所以要么选叶子,要么选叶子的父亲。显然选择父亲节点要更优一些

所以就可以直接贪心构造


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , tp , head[10005] , chose[10005] , ans ;
struct Path{
    int pre , to ;
}p[20005] ;

void In( int t1 , int t2 ){
    p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
    p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}

void dfs( int u , int f ){
    bool son = false ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v == f ) continue ;
        dfs( v , u ) ; son |= chose[v] ;
    } if( !son && !chose[u] && !chose[f] )
        chose[f] = true , ans ++ ;
}

void solve(){
    dfs( 1 , 1 ) ;
    printf( "%d" , ans ) ;
}

int main(){
    scanf( "%d" , &N ) ;
    for( int i = 1 , u , v ; i < N ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        In( u , v ) ;
    } solve() ;
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80369730