Google Kick Start 2019 F轮题解

原文首发自彼得攀的小站

查阅更多的题解,请点击
github传送门

本次的A题比较难,应该算leetcode中的Hard+,类似leetcode 265,但会更难一点:leetcode 265用二维dp即可,而A题需要三维dp

A. Flattening

题目地址

Solution

这道题比较难,做的时候感觉DP不易解,用了贪心,3个小时有一个半小时都在调这个,最后还是没调出来…

后面看了官方题解,果然还是用dp求解

解法

定义 w a l l i wall_i walli代表第i面墙的高度;定义所有的墙有t种高度,用 n u m [ i ] , i ∈ [ 0 , t ) num[i], i\in [0, t) num[i],i[0,t)来表示

看到题,比较容易想到尝试动态规划,二维的状态容易被想到:定义 d p [ i ] [ j ] dp[i][j] dp[i][j]代表前 i i i个元素,至多包含 j j j对不相同的相同的相邻数字时需要的最少改变次数。此时注意二维很难做状态转移:对于前 i − 1 i-1 i1个元素,再加入一个元素时,由 w a l l i − 1 wall_{i-1} walli1 w a l l i wall_{i} walli的取值,可以有多个状态转移的过程,很难分析,所以需要三维状态

d p [ i ] [ j ] [ m ] dp[i][j][m] dp[i][j][m]代表前 i i i个元素,至多包含 j j j对不相同的相邻数字, 且其第 i i i个元素为 n u m [ m ] num[m] num[m]时需要的最少改变次数,此时所求解为:
m i n ( d p [ n ] [ j ] [ m ] ) , f o r   j ∈ [ 0 , k ]   a n d   m ∈ [ 0 , t ) min(dp[n][j][m]), for\ j\in[0, k]\ and\ m\in[0, t) min(dp[n][j][m]),for j[0,k] and m[0,t)

状态转移方程为:
d p [ i ] [ j ] [ m ] = m i n { m i n ( d p [ i − 1 ] [ j ] [ m ] , d p [ i − 1 ] [ j − 1 ] [ n ( n ≠ m ) ] )   w a l l i = = n u m [ m ] m i n ( 1 + d p [ i − 1 ] [ j − 1 ] [ n ( n ≠ m ) ] , 1 + d p [ i − 1 ] [ j ] [ m ] ) w a l l i ≠ n u m [ m ] dp[i][j][m]=min \begin{cases} min(dp[i-1][j][m], dp[i-1][j-1][n(n\neq m)])& \text{ $wall_{i}== num[m]$}\\ min(1+dp[i-1][j-1][n(n\neq m)], 1+dp[i-1][j][m])& \text{$wall_i\neq num[m]$}\\ \end{cases} dp[i][j][m]=min{min(dp[i1][j][m],dp[i1][j1][n(n=m)])min(1+dp[i1][j1][n(n=m)],1+dp[i1][j][m]) walli==num[m]walli=num[m]
上述过程中:

  • w a l l i = n u m [ m ] wall_{i}= num[m] walli=num[m]时,有两种情况: w a l l i − 1 = w a l l i = n u m [ m ] wall_{i-1}=wall_i=num[m] walli1=walli=num[m],不会增加不一致的对数,所以为 d p [ i − 1 ] [ j ] [ m ] dp[i-1][j][m] dp[i1][j][m]; w a l l i − 1 ≠ w a l l i wall_{i-1}\neq wall_i walli1=walli, 不一致的对数会加一(即前i-1个元素中,最多j-1个不一致),所以是 d p [ i − 1 ] [ j − 1 ] [ n ( n ≠ m ) ] dp[i-1][j-1][n(n\neq m)] dp[i1][j1][n(n=m)]
  • w a l l i ≠ n u m [ m ] wall_{i}\neq num[m] walli=num[m]时, 代表第i个数一定会改变一次,所以需要在之前的状态加一

定义 m i n D [ i ] [ j ] = m i n ( d p [ i ] [ j ] [ l ] ) minD[i][j]=min(dp[i][j][l]) minD[i][j]=min(dp[i][j][l]), 其中 l ∈ [ 0 , t ) l\in [0, t) l[0,t), 上述状态转移方程可以简化为:
d p [ i ] [ j ] [ m ] = m i n ( d p [ i − 1 ] [ j ] [ m ] , m i n D [ i − 1 ] [ j − 1 ] ) + ( w a l l i ! = n u m [ m ] ) dp[i][j][m]=min(dp[i-1][j][m], minD[i-1][j-1])+(wall_i!=num[m]) dp[i][j][m]=min(dp[i1][j][m],minD[i1][j1])+(walli!=num[m])

初始状态:
d p [ 1 ] [ 0 ] [ m ] = { 0   w a l l 1 = = n u m [ m ] 1 w a l l 1 ≠ n u m [ m ] dp[1][0][m]= \begin{cases} 0& \text{ $wall_{1}== num[m]$}\\ 1& \text{$wall_1\neq num[m]$}\\ \end{cases} dp[1][0][m]={01 wall1==num[m]wall1=num[m]
m i n D [ 1 ] [ 0 ] = 0 minD[1][0]=0 minD[1][0]=0

复杂度分析

分析见上述状态转移方程, O ( N 2 K ) O(N^2K) O(N2K) time

代码

code

B. Teach Me

題目地址

Solution

解法

题目求的是(i, j)的个数,其中员工i能教j。令 m ( i ) m(i) m(i)代表员工i能指导的员工数,则所求结果为 ∑ i n m ( i ) \sum_i^n{m(i}) inm(i)。注意求 m ( i ) m(i) m(i)比较麻烦,令 m ′ ( i ) m'(i) m(i)代表i不能指导的员工数,则 m ( i ) = n − m ′ ( i ) m(i)=n-m'(i) m(i)=nm(i)。这里引入 m ′ ( i ) m'(i) m(i)是因为 m ′ ( i ) m'(i) m(i) m ( i ) m(i) m(i)更易求:当且仅员工j的技能包含员工i的所有技能时,员工i不能指导j,。(思想就类似概率论中,事件A发生的概率不易求,尝试求1-P(A))

可以利用二进制来枚举一个员工技能的子集,最多2^5个,统计所有员工技能的子集出现情况。之后就可以直接求出 m ′ ( i ) m'(i) m(i)

note:集合可以hash为一个数,然后利用hash table来存储。

复杂度分析

O ( 2 5 ∗ n ) O(2^5*n) O(25n) time.

代码

code

C. Spectating Villages

题目地址

Solution

解法

Test 1的暴力枚举在这里

题目中的村庄构成了一个树,将任意一个村庄作为根节点,即可将该树转化为一个有根树,对村庄从0到V-1编号,不妨设村庄0为根。

每个村庄都有三个状态:建灯塔且亮、不建灯塔且亮、不建灯塔且不亮。可以用树形dp来枚举这三个状态,令 c h i l d ( i ) child(i) child(i)代表 i i i的子节点集合, v a l u e s ( i ) values(i) values(i)代表 i i i的权值:

  • d p [ i ] [ 0 ] = ∑ j ∈ c h i l d ( i ) m a x ( d p [ j ] [ 0 ] , d p [ j ] [ 1 ] ) dp[i][0]=\sum_{j\in child(i)}max(dp[j][0], dp[j][1]) dp[i][0]=jchild(i)max(dp[j][0],dp[j][1]):
    村庄i不建灯塔且不亮,此时所有的子节点不能建灯塔
  • d p [ i ] [ 1 ] = { ∑ j ∈ c h i l d ( i ) m a x ( d p [ j ] [ 0 ] , d p [ j ] [ 1 ] , d p [ j ] [ 2 ] ) ∃ j ∈ c h i l d ( i ) , d p [ j ] [ 2 ] ≥ d p [ j ] [ 0 ] + d p [ j ] [ 1 ] ∑ j ∈ c h i l d ( i ) m a x ( d p [ j ] [ 0 ] , d p [ j ] [ 1 ] , d p [ j ] [ 2 ] ) + arg ⁡ max ⁡ j ∈ c h i l d ( i ) ( d p [ j ] [ 2 ] − d p [ j ] [ 0 ] , d p [ j ] [ 2 ] − d p [ j ] [ 1 ] ) e l s e dp[i][1]= \begin{cases}\sum_{j\in child(i)}max(dp[j][0], dp[j][1], dp[j][2])& \exists{j\in child(i)}, dp[j][2]\geq dp[j][0]+dp[j][1]\\ \sum_{j\in child(i)}max(dp[j][0], dp[j][1], dp[j][2])+\arg \max_{j\in child(i)}(dp[j][2]-dp[j][0],dp[j][2]-dp[j][1]) & else \end{cases} dp[i][1]={jchild(i)max(dp[j][0],dp[j][1],dp[j][2])jchild(i)max(dp[j][0],dp[j][1],dp[j][2])+argmaxjchild(i)(dp[j][2]dp[j][0],dp[j][2]dp[j][1])jchild(i),dp[j][2]dp[j][0]+dp[j][1]else
    村庄i不建灯塔且亮,此时子节点至少有一个建灯塔(此时取子树权值之和最大的情况,即选择子树亮灯和不亮之间最大,注意至少有一个子树需要亮灯
  • d p [ i ] [ 2 ] = v a l u e s [ i ] + ∑ j ∈ c h i l d ( i ) m a x ( d p [ j ] [ 1 ] , d p [ j ] [ 2 ] ) dp[i][2]=values[i]+\sum_{j\in child(i)}max(dp[j][1], dp[j][2]) dp[i][2]=values[i]+jchild(i)max(dp[j][1],dp[j][2])
    村庄i建灯塔且亮,此时子节点可以不建灯塔,或是建任意多的灯塔,但是所有的子节点均被点亮根节点会影响子节点的状态:若子节点未建灯塔且不亮,其状态会变为亮,即 d p [ j ] [ 0 ] 变 为 d p [ j ] [ 0 ] + v a l u e s [ j ] dp[j][0]变为dp[j][0]+values[j] dp[j][0]dp[j][0]+values[j]

由于知道根节点,可以从上到下递归,所求结果为 m a x ( d p [ 0 ] [ 0 ] , d p [ 0 ] [ 1 ] , d p [ 0 ] [ 2 ] ) max(dp[0][0], dp[0][1], dp[0][2]) max(dp[0][0],dp[0][1],dp[0][2]),dp的初始状态为叶子节点的状态:

  • d p [ l e a f ] [ 0 ] = 0 dp[leaf][0]=0 dp[leaf][0]=0
  • d p [ l e a f ] [ 1 ] = I N T _ M I N dp[leaf][1]=INT\_MIN dp[leaf][1]=INT_MIN: 对于叶子节点,该状态并没有什么实际上的意义
  • d p [ l e a f ] [ 2 ] = v a l u e s l e a f dp[leaf][2]=values_{leaf} dp[leaf][2]=valuesleaf
复杂度分析

解法中,利用图的DFS来模拟有根树的遍历过程,所以时间复杂度为: O ( n ) O(n) O(n) time.

代码

code

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值