力扣双周赛-174
题一和题二都很简单直接秒了,可惜我昨天在看斯诺克,边看边做,就没打算做最后一道困难题了,其实挺简单的。以后尽量双周赛都参加一下,锻炼一下我的辣鸡算法能力。
题三:100918. 交替按位异或分割的数目
题意:给一个数列数组,把他划分为若干个子数列,每个数列的异或和在target1和target2之间轮转。
之前对异或不够熟悉。异或是位运算,相同为0,不同为1,从而有:
- 自己异或自己为零,即:
a ^ a = 0 - 异或的逆运算是自己,因此:若
a ^ b = c则a = c ^ b,即abc任意两个进行异或都等于第三个。
这个题用前缀异或和(类似前缀和)的特性,设置2个dp数组分别是dp1和dp2,表示:dp1[sum](或dp2[sum])表示分割前缀异或和为sum的序列,分割成满足target1和target2交错,且最后一组的异或值为target1(或target2)的序列组的方法数;
这样之后,当遍历到输入参数nums的下标i时,此时dp1[sum]和dp2[sum]便表示:在下标i之前,分割前缀异或和为sum、且以target1或target2结尾的方法数。
因此状态转移方程,就是将下标i的情况数添加进去,先算出来nums[:i]的前缀异或和,记作pre_sum,本轮只需计算出下标i的情况数并累加到dp1[pre_sum]和dp2[pre_sum]中。
如果nums[:i]划分后,最后一组的前缀异或和是target1,则需要以pre_sum ^ target1为异或和的某个前缀。例如,现在i为5,即num[0:5],如果num[0:2]的前缀和是pre_sum ^ target1,则xor(nums[2:5])的前缀和必然是target1。dp2[pre_sum ^ target1]可能不仅包含nums[0:2]的情况,还可能有nums[0:3]等等,而他们都满足最后一组的前缀异或和为target2。所以此处其实对下标进行压缩了。
状态转移方程:
1 | dp1[pre_sum] += dp2[pre_sum ^ target1] |
再设置一个边界条件,让dp2[0] = 1,即找第一段前缀和为target1的序列时,我们会遍历到pre_sum = target1,此时dp2[pre_sum ^ target1] = dp[0],令其为1则充当了整个算法的数值起点,否则累加半天全是零;当然也可以设置当pre_sum第一次与target1相等时特殊处理dp2[pre_sum ^ target1]
1 | class Solution: |
题四:100954. 翻转树上最少边
题意:给一个树有n-1个节点,每个节点有个当前bool值和目标布尔值,可以选择一条边,将边的两个端点进行翻转。问最少的翻转方案。
从叶节点开始翻转,因为叶子的翻转情况是确定的。使用后序遍历,即自下而上的顺序,从叶子开始算起。对于每个节点,dfs函数返回它是否需要翻转,当遍历到叶子时,该函数的结果直接是确定的。
如果是非叶子,则考虑它的所有children,每一个需要翻转的children会造成自己翻转一次,自己的值是否和目标值相等也会导致翻转一次,考虑完这些就可以返回了。
1 | class Solution: |





