(题面可自行跳过)
描述
给一棵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 有边相连。
输出
仅一个数,即着色结点数的最小值
样例输入 [复制]
5 3
0
1
0
1 4
2 5
4 5
3 5
样例输出 [复制]
2
数据规模:
m<=10000 n<=5000
思路:
本身不难,但是个人认为比较经典
状态转移不一定 要完全保留 子状态 ,可以进行 改变,满足最优性
只需要 子状态是平行比较中最优的
首先容易想到贪心:
对于一个有根树,
显然尽量向上染色能影响的节点最多。。 仅有当前节点的儿子中 不同颜色都有,就把数量少的颜色一个个染, 数量多得作为一个传上去。
但是,这样无法处理两种颜色数量相同的情况。。
所以转向dp
f[i][k]+= min(f[son][k]-1,f[son][k^1])
k代表颜色 (0或1) , i是当前子树的根节点
思考状态定义:显然为 以i为根节点的子树最少次数。
此题的最优子结构的染色方法肯定要改变,
所以 思考如何当前步操作,使最优子结构可以更新出当前子结构。
结合贪心,我们还是把染色点尽量上放,那么在确定当前节点染色 (0或1),可以把染相同颜色的子节点都统一染在 当前点,总数依次-1
不同的就保留
最后所得值+1(当前要染色)
第二步:
思考换根, 对于当前根的儿子,如果有不同,仅限于这两个结点都染色的情况。
根据贪心策略(或者状态转移)
显然相邻两个节点颜色不相同(否则染在根上即可),那么其中任意一个作为根,
答案相同。
所以不需要换根。。。
//代码中并没有用数组,直接用了pair。。还要慢一点。。。
代码:
#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define fi first
#define se second
using namespace std;
const int maxn=1e4+10;
typedef pair<int,int> T;
vector <int> G[maxn];
int col[maxn];
//f[i][k]+= min(f[son][k]-1,f[son][k^1])
inline T dfs(int u,int fa){
T t,ans;
if(col[u]!=-1){
return col[u]==0? make_pair(1,0x5f5f5f5f) : make_pair(0x5f5f5f5f,1);
}
for(int i=0;i<G[u].size();++i){
int v=G[u][i];if(v==fa)continue;
t=dfs(v,u);
ans.fi+=min(t.fi-1,t.se);
ans.se+=min(t.fi,t.se-1);
}
ans.fi+=1;ans.se+=1;
return ans;
}
signed main (){
memset(col,-1,sizeof col);
int n,m;
sf("%d%d",&m,&n);
for(int i=1;i<=n;++i){
sf("%d",&col[i]);
}
for(int i=1;i<m;++i){
int a,b;sf("%d%d",&a,&b);
G[a].push_back(b);G[b].push_back(a);
}
T ret=dfs(n+1,-1);
pf("%d",min(ret.fi,ret.se));
return 0;
}