解法
树的最小支配集问题,参考:
这里
解法一:贪心法
先做DFS【就是先根遍历】,取得遍历节点编号及其父节点编号
然后倒过来,逐一判断:
最小支配集
:
【何时加入】若当前节点没被覆盖,且它的父节点不在支配集里
【加入谁】当前节点的父节点
【标记什么】当前节点,当前节点的父节点,当前节点父父节点都标记为已覆盖
最小点覆盖
:
【何时加入】若当前节点及其父节点均未覆盖
【加入谁】当前节点的父节点
【标记什么】当前节点,当前节点的父节点标记为已覆盖
最大独立集
:
【何时加入】若当前节点未覆盖
【加入谁】当前节点
【标记什么】当前节点,当前节点的父节点标记为已覆盖
注意:
根节点的父节点为自己,只有【最小点覆盖】不检查根节点,其它都检查
class Solution(object):
def minCameraCover(self, root):
"""
:type root: TreeNode
:rtype: int
"""
from collections import defaultdict
p = []
def dfs(prev,root):
if root:
i = len(p)
p.append(prev)
dfs(i,root.left)
dfs(i,root.right)
dfs(0,root)
n = len(p)
if n==1:
return 1
mark = set()
used = set()
ans = 0
for i in xrange(n-1,-1,-1):
if i not in mark:
if p[i] not in used:
used.add(p[i])
ans += 1
mark.add(i)
mark.add(p[i])
mark.add(p[p[i]])
return ans
解法二:树形动态规划
最小点覆盖和最大独立集都比较简单,只有2个状态,分别是标记和不标记
对于最小支配集,每个子树3个状态:
- 状态0:根被标记,整个子树都被覆盖的最小标记数目
- 状态1:根未被标记,整个子树被覆盖,且至少有一个子节点被标记
- 状态2:根未被标记,整个子树被覆盖,且没有子节点被标记
每个状态如何递归:
-
状态0:每个子树的3个状态的最小值之和+1:
dp[root][0] = min(dp[root.left])+min(dp[root.right])+1
-
状态1:【如果根没有孩子,
dp[root][1]
为INF】根未被标记时子树不可以是状态3。所以是前两个状态取最小值之和。但是如果每个子节点的最小值都是状态1,那么就和根是状态1的假设矛盾了。所以要挑一个节点取状态0,这必然会使结果增加,那么选增加得最少的那个,即
dp[u][0]-dp[u][1]
最小的那个指定为状态0。不可能是状态3是因为如果根没有标记,儿子没有标记,根还被覆盖了,可能是根的父亲标记了,但是如果孙子也没有标记,那么儿子就不可能被标记,矛盾。
-
状态2:此时子树只可能是状态1。
dp[root][2] = dp[root.left][1]+dp[root.right][1]
最后取根节点的状态1和状态0里最小的那个
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def minCameraCover(self, root):
"""
:type root: TreeNode
:rtype: int
"""
INF = 0x7fffffff
def solve(root):
if root.left and root.right:
left = solve(root.left)
right = solve(root.right)
return min(left)+min(right)+1, min(left[0]+min(right[:-1]), min(left[:-1])+right[0]),left[1]+right[1]
res = None
if root.left:
res = solve(root.left)
elif root.right:
res = solve(root.right)
if res!=None:
return min(res)+1, res[0], res[1]
return 1, INF, 0
return min(solve(root)[:-1])