zoukankan      html  css  js  c++  java
  • [CQOI2009]叶子的染色【性质+树形Dp】

    Online JudgeBzoj1304Luogu P3155

    Label:无根树,树形Dp

    题目描述

    给定一棵(N)个节点的无根树,它一共有(K)个叶子节点。你可以选择一个度数大于1的节点作为根,并对整棵树进行染色(对于每个节点可以染黑/白,或选择不染),着色方案需满足以下约束:

    1.每个叶子节点到树根的路径上都至少存在1个有色节点; 2.对于编号为(i)的叶子节点,给定一个(c[i]),表示从叶子节点到树根路径上第一个遇到的有色节点的颜色(0:黑,1:白)

    (M<=10000) (N<=5021)

    输入

    第一行包含两个正整数(N,K)

    结点编号为(1,2,…,N),其中编号(1,2,… ,K)是叶子。

    以下(K)行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。

    以下(m-1)行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。

    输出

    仅一个数,即着色结点数的最小值。

    样例

    Input

    5 3
    0
    1
    0
    1 4
    2 5
    4 5
    3 5
    

    Output

    2
    

    题解

    唯一的难度在于是棵无根树

    part1

    如果是棵有根树怎么做。

    [1]状态定义

    定义状态(f[x][0/1])表示节点(x)染为黑/白,以其为根的子树所需的最小染色数。

    为什么第二维不再弄个(2)表示不染色时的状态呢(当然这样也可以做,但没必要),在之后的转移中我们有一个"删除儿子颜色"的操作,也就是说先染上色,到时候再根据最优性考虑删除颜色。

    [2]初始化、终态

    一开始所有节点,不论选黑选白都至少选了一个有色节点,赋值为1;但对于叶子节点,将他所不能第一个遇到的那个颜色赋值为INF,后面转移时就不会用到这个状态了。

    初始化 : (f[x][0/1]=1)(f[leaf][!c[leaf]]=INF)

    最后答案为(ans=max(f[root][0],f[root][1]))。只用考虑根节点染黑染白的情况。为什么无需考虑根节点不染色的情况呢,因为显然将树根染上颜色比将其某个子孙染上同种颜色会更优。

    [3]转移

    一遍dfs搞一下整棵树,对于非叶子节点x,其儿子为son。

    如果x,son同色,则让x保留颜色,son删除颜色更优(x包含的范围更广)。如果不同色则都保留。两者取较小值传给x。

    (f[x][0]+=min(f[son][0]-1,f[son][1]);)

    (f[x][1]+=min(f[son][1]-1,f[son][0]);)

    part2

    性质:在本题中,根节点的选取对于答案没有影响。

    证明:类似换根的想法,设一开始的树根为(x),现在要将他的某个儿子(son)设为根。

    根据上面分析,在(x)做根的最优方案中,

    1.x与son不会染相同的颜色;

    2.x一定会染色。

    不妨设那时x染为黑。

    A.当son染为白时,换根后两者分别可能会影响的范围根本没有变化,所以颜色都不用改变,答案当然不会变。

    B.当son不染色时,原来x可能会影响的范围是整棵树,换根后,只需将x的颜色转给son,x本身不染色即可保证树根可能影响的范围不变,这样只有这两点的颜色交换了,染色总数依然不变。

    所以随便挑一个非叶子节点做根就好了。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=10010;
    vector<int>e[N];
    int f[N][2],n,k;
    void dfs(int x,int fa){
    	if(x<=k)return;
    	for(int i=0;i<e[x].size();i++){
    		int y=e[x][i];if(y==fa)continue;
    		dfs(y,x);
    		f[x][0]+=min(f[y][0]-1,f[y][1]);
    		f[x][1]+=min(f[y][1]-1,f[y][0]);
    	}	
    }
    int main(){
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;i++)f[i][0]=f[i][1]=1;
    	for(int i=1,w;i<=k;i++){
    		scanf("%d",&w),f[i][!w]=1e9;
    	}
    	for(int i=1,u,v;i<n;i++){
    		scanf("%d%d",&u,&v);
    		e[u].push_back(v);e[v].push_back(u);
    	}
    	dfs(n,0);
    	printf("%d
    ",min(f[n][0],f[n][1]));
    }
    
  • 相关阅读:
    使用npm安装包失败的解决办法(使用npm国内镜像介绍)
    JavaScript的变量、作用域和内存问题
    JavaScript的基本概念
    在Html中使用JavaScript
    JavaScript简介
    C++为了兼容,所以并不是纯面向对象编程语言
    C++四种不同的对象生存方式
    Java BigDecimal使用
    ext 对齐
    ext grid 子表格
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11522475.html
Copyright © 2011-2022 走看看