清华大学软件学院2020保研机试题

今年突然变成四道题了,还是三个小时,说实话时间感觉有点不够用。但是变成实时oj了,可以直观看多少分。倒数第二题最后没想出来解法,只有暴力搜索混分了。

1. 监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。

https://leetcode-cn.com/problems/binary-tree-cameras/solution/
leetcode 原题

贪心就好了:找任一个叶结点,把照相机放到父结点上。然后删掉他的祖父、父、他和他的兄弟,然后在新的树(或者说森林)重新找叶结点。

O ( N ) O(N) O(N)

2. 因子递推

给N < M < 1e5, 从p出发走一步可以到p+k( k是p 因数且k != 1 且 k != p),求N到M最小步数

递推就完了 O ( N 1.5 ) O(N^{1.5}) O(N1.5)

//伪代码,记得初始化之类的
#define MAX 10000
#define INBOUND(i) i <= MAX 
s[MAX + 1];//代表从s[i] N出发到达i的最少步数
s[N] = 0;
int solve(){
    
    
	for(int i = N; i <= M; i ++){
    
    
		int root = ceil(sqrt(i)) + 1;
		for(int j = 2; j < root; j ++){
    
    
			if(i % j == 0){
    
    
				int k = i / j;
				INBOUND(i + j) && s[i + j] = min(s[i + j], s[i] + 1);
				INBOUND(i + k) && s[i + k] = min(s[i + k], s[i] + 1);
			}
		}
	} 
	return s[M];
}

3. 最大表达式

给一串共N个数字排成一环,数字间有边,边上有乘号或者加号,去掉一条边之后,将两边数字按照边上算符可以合并成一新的个数字,可以递归地删除边最终得到一个数字,求第一次去掉哪一条边能获得最大数字?

例子 1 + 2 
	X   X
	4 + 5
第一次删除12 中间的+号有:2 X 5 + 4 X 1,再分别删掉 第二个、第一个、第三个得到最大值18
N <= 50

动态规划
第一次删哪条边不是重点,遍历就完了

重要的子问题
排成一列的数字,中间有+或x,问合并后的最大值?

使用 M [ i , j ] M[i,j] M[i,j] 代表第 i i i j j j个数字合并后可以产生的最大正值, N [ i , j ] N[i,j] N[i,j]代表 i i i j j j个数字合并后可以产生的最大负值,就有
M [ i , j ] = M a x o p ∈ O p { M [ i , k ] + M [ k , j ] ( o p [ k ] = ′ + ′ ) , M a x ( M [ i , k ] ∗ M [ k , j ] , N [ i , k ] ∗ N [ k , j ] ) ( o p [ k ] = ′ × ′ ) } M[i,j] = Max_{op \in Op}\{ M[i,k] + M[k,j] (op[k] ='+'),\\ Max(M[i, k] * M[k,j] , N[i,k] * N[k,j])(op[k] ='\times')\} M[i,j]=MaxopOp{ M[i,k]+M[k,j](op[k]=+),Max(M[i,k]M[k,j],N[i,k]N[k,j])(op[k]=×)}
(就是说,当符号为+时,正最大值就是两边最大值的和;符号为x时,取正最大值的积和负最大值的积的最大值)
N [ i , j ] = M i n o p ∈ O p { N [ i , k ] + N [ k , j ] ( o p [ k ] = ′ + ′ ) , M a x ( M [ i , k ] ∗ N [ k , j ] , N [ i , k ] ∗ M [ k , j ] ) ( o p [ k ] = ′ × ′ ) } N[i,j] = Min_{op \in Op}\{ N[i,k] + N[k,j] (op[k] ='+'),\\ Max(M[i, k] * N[k,j] , N[i,k] * M[k,j])(op[k] ='\times')\} N[i,j]=MinopOp{ N[i,k]+N[k,j](op[k]=+),Max(M[i,k]N[k,j],N[i,k]M[k,j])(op[k]=×)}
一个意思

复杂度 O ( N 3 ) O(N^3) O(N3)

4. 树上钟同步

有一棵树,树上每个节点有一个钟(取值范围0-11),钟上有一个初始值。
一个人从某个点出发(出发时不会自增1)
只能经过边到达另一个点。
到达某点时,该点的值自增一(超过11置为0)。
求所有的可行初始节点,使得从这些节点出发存在一条路径(可重复经过边经过点)可以将树上所有钟的值都置为0。

节点数不超过2500

强子命题

考虑要求这个人最后必须回到初始点的命题,是否存在一条可以将钟都置为0的路径。

此时他一定在每条边都走了偶数次,且两个方向的次数相等

这样的话,通过叶结点 A A A计算要置为0对应边应该走的次数 k = 12 − i k = 12 - i k=12i,并将 k k k加到相邻点上后删去 A A A,迭代地这样做,最后一个点的值为0就存在,否则不存在。

原命题 = 一条不重复路径+强子命题

因为某点最终的值和他到达该点的时间无关,只和次数相关。并且走 k k k次等价于 12 + k 12+k 12+k次。
再考虑原问题,他走的路径一定可以等价成一条不重复的路径再加上一次从终点开始回到终点的路径。
那么我们只需要dfs遍历所有的不重复路径,最后再判定强子命题能否成立就可以了。时间复杂度是 O ( N 3 ) O(N^3) O(N3),会超时。

改进,动态更新

其实我们再看强子命题计算中,当我们使用拓扑排序排好了一个排列后 a [ 1... n ] a[1...n] a[1...n],最后一个点的值也一定能展开为 ( − 1 ) ( k a i ) c l o c k a i (-1)^{(k_{a_i})}clock_{a_i} (1)(kai)clockai,于是我们能将每个 k a i k_{a_i} kai 记录下来,将初始最后一个节点值也记录下来。之后每次对点加一操作时,我们就对最后一个节点值加 ( − 1 ) ( k a i ) (-1)^{(k_{a_i})} (1)(kai),当最终点值变为0时,说明我们找到了一个好的路径。

时间复杂度下降到 O ( N 2 ) O(N^2) O(N2),可以ac了

一个不保证正确性的剪枝

现场的时候,我直接认为若 A A A是可行的初始节点,那么其他节点出发的不重复路径一定不能经过 A A A,虽然让我A了,但现在想起来可能会有点问题。

猜你喜欢

转载自blog.csdn.net/zhongershuchong/article/details/108622480