Weekly Contest 117

1.Univalued Binary Tree

解题思路:递归判断下即可。

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func isUnivalTree(root *TreeNode) bool {
	if root == nil {
		return true
	}
	if root.Left != nil && root.Val != root.Left.Val {
		return false
	}
	if root.Right != nil && root.Val != root.Right.Val {
		return false
	}
	ok := true
	ok = ok && isUnivalTree(root.Left)
	ok = ok && isUnivalTree(root.Right)
	return ok
}
2. Numbers With Same Consecutive Differences

解题思路:dfs 枚举即可。

func numsSameConsecDiff(N int, K int) []int {

	if N == 1 {
		return []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	}

	var res []int
	var bits [10]int
	var dfs func(i int)
	dfs = func(idx int) {
		if idx == N {
			num := 0
			for i := 0; i < N; i++ {
				num = num*10 + bits[i]
			}
			res = append(res, num)
			return
		}
		if idx == 0 {
			for i := 1; i <= 9; i++ {
				bits[idx] = i
				dfs(idx + 1)
			}
		} else {

			if bits[idx-1]+K <= 9 {
				bits[idx] = bits[idx-1] + K
				dfs(idx + 1)
			}

			// 注意此处 K 为 0 时,只能转移一次,否则会求解出重复元素
			if bits[idx-1]-K >= 0 && K > 0 {
				bits[idx] = bits[idx-1] - K
				dfs(idx + 1)
			}
		}
		return
	}
	dfs(0)
	return res
}
3. Vowel Spellchecker

解题思路:分类处理即可。

func spellchecker(wordlist []string, queries []string) []string {

	var ans []string
	if len(wordlist) == 0 {
		ans = make([]string, len(queries))
		return ans
	}

	sameMap := map[string]string{}
	for i := range wordlist {
		sameMap[wordlist[i]] = wordlist[i]
	}
	capMap := map[string][]string{}
	for i := range wordlist {
		word := strings.ToLower(wordlist[i])
		capMap[word] = append(capMap[word], wordlist[i])
	}
	vowelMap := map[string][]string{}
	for i := range wordlist {
		word := strings.ToLower(wordlist[i])
		var bs []byte
		for j := range word {
			if word[j] == 'a' || word[j] == 'i' || word[j] == 'o' ||
				word[j] == 'e' || word[j] == 'u' {
				bs = append(bs, '*')
			} else {
				bs = append(bs, word[j])
			}
		}
		bss := string(bs)
		vowelMap[bss] = append(vowelMap[bss], wordlist[i])
	}

	for i := range queries {
		if _, ok := sameMap[queries[i]]; ok {
			ans = append(ans, queries[i])
			continue
		}
		lq := strings.ToLower(queries[i])
		if _, ok := capMap[lq]; ok {
			ans = append(ans, capMap[lq][0])
			continue
		}
		bs := []byte{}
		for j := range lq {
			if lq[j] == 'a' || lq[j] == 'e' || lq[j] == 'i' ||
				lq[j] == 'o' || lq[j] == 'u' {
				bs = append(bs, '*')
			} else {
				bs = append(bs, lq[j])
			}
		}
		bss := string(bs)
		if _, ok := vowelMap[bss]; ok {
			ans = append(ans, vowelMap[bss][0])
			continue
		}
		ans = append(ans, "")
	}
	return ans
}
4. Binary Tree Cameras

解题思路:树形 DP。顺便梳理下一些相关问题求解的动态规划解法。

最小点覆盖集:选择最少点集合关联上树上的所有边。

  • 状态定义
    dp[i][0]:以 i 为根的子树且顶点 i 不属于点覆盖集中顶点的情况下最少的顶点数
    dp[i][1]:以 i 为根的子树且顶点 i 属于点覆盖集中顶点的情况下最少的顶点数
  • 状态转移
    dp[i][0] = SUM{dp[u][1]},u 为 i 的孩子节点
    dp[i][1] = SUM{min{dp[u][0], dp[u][1]}}

最大点独立集:选择最多点集合,其中任意顶点之间不存在边。

  • 状态定义
    dp[i][0]:以 i 为根的子树且顶点 i 不属于点独立集中顶点的情况下最多的顶点数
    dp[i][1]:以 i 为根的子树且顶点 i 属于点独立集中顶点的情况下最多的顶点数
  • 状态转移
    dp[i][0] = SUM{max{dp[u][0], dp[u][1]}},u 为 i 的孩子节点
    dp[i][1] = SUM{dp[u][0]}, u 为 i 的孩子节点

最小点支配集:选择最少的顶点集合使得其余顶点均与该点支配集中的顶点相邻。

  • 状态定义
    dp[i][0]:以 i 为根的子树且 i 属于支配集且 i 的子树均被覆盖的情况下需要的最少节点数
    dp[i][1]:以 i 为根的子树且 i 不属于支配集且 i 的子树均被覆盖且至少一个孩子节点覆盖掉 i 的情况下的最少节点数
    dp[i][2]:以 i 为根的子树且 i 不属于支配集且 i 的子树均被覆盖且不存在一个孩子节点覆盖掉 i 的情况下最少的节点数
  • 状态转移
    dp[i][0] = SUM(min(dp[u][0], dp[u][1], dp[u][2])) + 1,其中 u 为 i 的孩子节点
    dp[i][1] 则需要分两种情况讨论,当 i 无孩子节点时,容易 dp[i][1] = inf;当 i 存在孩子节点时,dp[i][1] = SUM(min(dp[u][0], dp[u][1]))且要保证其孩子节点中至少存在一个值是 dp[u][0],因为要保证 i 点被孩子节点覆盖掉嘛
    dp[i][2] = SUM(dp[i][1]),注意一个节点 i 最多能被其父节点覆盖掉,不能被其祖先节点覆盖,因此对于任意一条树上的路径肯定不会存在连续三个节点均不在支配集中`

PS:画棵深度为三层的满多叉树便很容易理解上述的状态转移方程。

const (
	inf = 1000010
)

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func minCameraCover(root *TreeNode) int {

	if root == nil {
		return 0
	}

	dp := map[*TreeNode]map[int]int{}
	var dfs func(r *TreeNode)
	dfs = func(r *TreeNode) {
		dp[r] = map[int]int{}
		if r.Left == nil && r.Right == nil {
			dp[r][0] = 1
			dp[r][1] = inf
			dp[r][2] = 0
			return
		}
		dp[r][0] = 1
		if r.Left != nil {
			dfs(r.Left)
			dp[r][0] += minInt(minInt(dp[r.Left][0], dp[r.Left][1]), dp[r.Left][2])
			dp[r][2] += dp[r.Left][1]
		}
		if r.Right != nil {
			dfs(r.Right)
			dp[r][0] += minInt(minInt(dp[r.Right][0], dp[r.Right][1]), dp[r.Right][2])
			dp[r][2] += dp[r.Right][1]
		}
		if r.Left == nil {
			dp[r][1] = dp[r.Right][0]
			return
		}
		if r.Right == nil {
			dp[r][1] = dp[r.Left][0]
			return
		}
		dp[r][1] = minInt(minInt(dp[r.Left][0]+dp[r.Right][1], dp[r.Left][1]+dp[r.Right][0]), dp[r.Left][0]+dp[r.Right][0])
	}
	dfs(root)
	return minInt(dp[root][0], dp[root][1])
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值