luogu3155 [CQOI2009]叶子的染色
题目大意
给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。 对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
题解
选什么根都一样
证明思路是先证明一步,然后由此推广到所有可能。我们先证明:把一个根(以后简称“原根”)换为它的儿子(以后简称“新根”),最优情况不变。我们假定原根与新根的颜色要么不变要么交换。首先原根的、不以新根为根的子树内的叶子节点是不会受到影响的,因为它们到根的路径延长了,该遇到颜色的节点总会遇到的。那么以对于新根为子树内的叶子节点呢?如果原根与新根都有颜色,那么结果毫无影响,因为原根与新根的颜色肯定不相同(如果相同,把新根变为透明依然满足要求,结果却变小了),此时这个子树内不可能存在一个叶子节点,它到根节点路径上的第一个颜色为c[那个叶子]的结点(以后简称那个叶子“依赖”的结点)就是原根(因为经过了一个颜色不相同的结点新根)。唯一特殊的情况便是原根有颜色,新根是透明。换为新根后,依赖于原根的叶子结点便没有了依赖的对象了。怎么办?把原根与新根颜色交换就都满足要求了。
综上所述,把现有的根换为当前根的儿子,结果不变。那么随便找另一个点作为根,那个点必然是当前根的儿子的儿子的儿子的儿子...,故答案也不变。所以,选什么根都一样。
动规
树上求最值,往往要用树上DP。树上DP的子问题往往在子树中。问题在于:树与子树间的联系是什么?我们的思维可能会卡在我们的经验,也就是动规的子问题都是往往都是在局部将问题彻底解决。而这道题不一样,一个子树内的叶子结点所依赖的结点很有可能不在子树内。所以我们应当这样思考:当前问题的解决方案由子问题怎样改造而来的?
我们先暂时用cur->DP来表示以cur为子树时,最少多少个结点需要染色。我们此时需要画一画。这时,我们发现在一个子问题中,根节点被染色的最优方案一定属于最优方案的集合中,因为任何根节点没有被染色的最优方案都可以通过将离根节点最近的染色结点的颜色转移到根节点上(以后简称“颜色转移根操作”)的方法转化为根节点被染色的最优方案。所以我们就令所有子问题的解根节点都被染色。当然如果只给一个DP问题不单纯,我们要将DP分为DP[0],DP[1]表示根节点染成黑色和染成白色时的最优解。首先要清楚一点,拿DP[0]为例,对于一个子树,如果我们要选择子树根为黑色的方案,因为cur就是黑色,所以在以cur为根的染色方案中,该子树根将要改为透明,故该子树内染色的节点数为cur->Son->DP[0]-1;如果要选择子树根为白色的方案,在以cur为根的方案中,该子树的染色方案不变,仍然为cur->Son->DP[1]。所以cur->DP[0]=1+sum(i){min{cur->Son[i]->DP[0]-1,cur->Son[i]->DP[1]}}。cur->DP[1]同理。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAX_NODE = 10010, INF = 0x3f3f3f3f; int TotLeaf, TotNode; struct Node { vector<Node*> Next; int ColorWant; int F[2]; }_nodes[MAX_NODE]; void Dfs(Node *cur, Node *fa) { if (cur - _nodes <= TotLeaf) return; cur->F[0] = cur->F[1] = 1; for (int i = 0; i < cur->Next.size(); i++) { Node *next = cur->Next[i]; if(next == fa) continue; Dfs(next, cur); cur->F[0] += min(next->F[0] - 1, next->F[1]); cur->F[1] += min(next->F[1] - 1, next->F[0]); } } int main() { scanf("%d%d", &TotNode, &TotLeaf); for (int i = 1; i <= TotLeaf; i++) scanf("%d", &_nodes[i].ColorWant); for (int i = 1; i <= TotLeaf; i++) { _nodes[i].F[_nodes[i].ColorWant] = 1; _nodes[i].F[!_nodes[i].ColorWant] = INF; } for (int i = 1; i <= TotNode - 1; i++) { int u, v; scanf("%d%d", &u, &v); _nodes[u].Next.push_back(_nodes + v); _nodes[v].Next.push_back(_nodes + u); } for (int i = TotLeaf + 1; i <= TotNode; i++) _nodes[i].F[0] = _nodes[i].F[1] = 0; Dfs(_nodes + TotLeaf + 1, NULL); printf("%d\n", min(_nodes[TotLeaf + 1].F[0], _nodes[TotLeaf + 1].F[1])); return 0; }