P3155 [CQOI2009]叶子的染色
题目描述
给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。 对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
输入输出格式
输入格式:
第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,...,m,其中编号1,2,... ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],...,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。
输出格式:
仅一个数,即着色结点数的最小值。
由题意可反应到应该是树形动归。
先考虑一个父节点和一个子节点。 若子节点的颜色和父节点相同, 那我们子节点不染色,撤销子节点的颜色, 显然不造成影响。弱父节点与子节点颜色不一样, 两个点都染色即可。
扩展到多个子节点显然也成立, 即遵照: 同色 - 1,异色不变 即可
所以归纳一下, 无论如何, 我们在讨论的这个父节点总要在这一步染色, (在以后会不会把颜色撤销还不知道), 所以我们用两个数组 \(dp[i][2]\) 来表示第 \(i\) 号点分别染黑色、白色的最少用点数。 此时分类讨论, 有状态转移方程: \[dp[u][0] = 1 + \sum \min(dp[v][0] - 1,dp[v][1])\]\[dp[u][1] = 1 + \sum \min(dp[v][1] - 1,dp[v][0])\]
边界显然在叶子节点: 对于有需要的节点, 染成需要颜色则为 \(1\) ,不需要颜色赋值为无穷大以排除影响。 其他节点因为在动归本次时会加一个 \(1\) ,故不是叶子节点时自己加上 \(1\) 。
P.s. 当然我们需要判断一下此点的儿子是否有需要颜色, 若所有子节点都没有需要, 则将自己置为 \(0\) 这题数据较水加上偷点小懒, 这里就不特判啦
Code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int RD(){
int flag = 1, out = 0;char c = getchar();
while(c < '0' || c > '9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 1000019, INF = 1e9 + 19;
int head[maxn], nume = 1;
struct Node{
int u, v, dis, nxt;
}E[maxn << 3];
void add(int u, int v, int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int num, na;
int dp[maxn][2];
int root;
void dfs(int u, int F){
bool flag = 0;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(v == F)continue;
dfs(v, u);
flag = 1;
dp[u][0] += min(dp[v][0] - 1, dp[v][1]);
dp[u][1] += min(dp[v][1] - 1, dp[v][0]);
}
if(flag)dp[u][0]++, dp[u][1]++;
}
int main(){
num = RD();na = RD();
for(int i = 1;i <= na;i++){
int x = RD();
if(x == 0)dp[i][0] = 1, dp[i][1] = INF;
else dp[i][0] = INF, dp[i][1] = 1;
}
for(int i = 1;i <= num - 1;i++){
int u = RD(), v = RD();
add(u, v, 1),add(v, u, 1);
}
root = na + 1;
dfs(root, -1);
int ans = min(dp[root][0], dp[root][1]);
printf("%d\n", ans);
return 0;
}
}
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9337839.html