[Google Kickstart 2020][校招笔试][Round F]全部题目+题解

题1:ATM Queue

题意

N个人排队取钱(初始顺序1,2,3……N),第i个人想取Ai元,但是ATM机每次只能取X元。如果有人要取的钱多于X元,就需要到队尾重新排队,直到取完离开。求这N个人取完钱离开队伍的次序。

数据范围:

小数据:所有数字在100以内
大数据:
N<=105
Ai<=109
X<=109

分析

笨办法是模拟,但对于大数据是要超时的,因此要避免模拟、直接算出这些人的顺序。

对于第i个人,他需要取A[i]/X(上取整)轮。第一轮的人一定比第二轮的人先走完,而同一轮的人中编号小的人会先离开(他在每一轮中都会排在编号大的人的前面)。

于是我们可以预处理出每个人的结束轮次号,可用(轮次号,人编号)这个数对来表示,然后从小到大排序(先按轮次号从小到大,再按人编号从小到大),最后依次输出数对中的人编号即可。

题2:Metal Harvest

题意

数轴上有N个不重叠区间,问最少可以用多少个长度为K的线段实现全部覆盖。

数据给出N个区间的起点Si、终点Ei,和K值,输出最少需要线段的个数。

数据范围

N<=109
K<=109
1<=S,E<=109

解法

贪心。对区间从左到右排序,最开始从第一个区间的左端点处开始放线段,直到覆盖第一个区间(线段个数可用上取整除法算出),然后再看上个线段是否覆盖了第二个区间,如果完全没有的话就再从第二个区间的左端点放,如果部分覆盖的话就从线段右端点开始放,直到覆盖第二个区间……非常简单。

题3:Painters’ Duel

题意

在这里插入图片描述

有一个三角形网格结构的博物馆,边长为S(图中是3),每个网格是一个房间。A同学和B同学分别在其中两个不同的房间。他们在玩一个比谁粉刷房间最多的游戏,游戏规则是:

  1. A和B轮流粉刷自己所在的房间(A先B后)。最开始A、B所在的房间都已粉刷,第一轮不需要刷。
  2. 每轮粉刷后,A和B需要移动至一间相邻的、未粉刷过的、且没有损坏的房间(损坏的房间列表由数据给出)
  3. 如果A或B无法移动,该同学的游戏结束,做轮空处理。
  4. 如果A和B都无法移动,游戏结束。
  5. 最后游戏的得分是:A粉刷的房间数-B粉刷的房间数。

A和B都采用最优策略试图赢得游戏:A尽力让得分最高,B尽力让得分最低。

数据给出A、B初始位置和损坏房间列表,请算出A一定能获得的最高得分(无论B怎么玩,A都能得到的最高得分)。

样例数据

格式

第1行:测试数据个数
每个数据第1行:S,A起始位置(行、列),B起始位置(行、列),损坏房间个数C
每个数据后C行:损坏房间位置列表

样例1

2
2 1 1 2 1 0
2 2 2 1 1 2
2 1
2 3
答案:2,0

样例2

2
3 3 4 2 1 2
2 3
3 1
3 3 2 2 3 2
2 1
3 1
答案:0,-1

数据范围

小数据:边长S<=2
大数据:边长S<=6

分析

一定要理解好什么是“两个人都采取最优策略”、“一定能得到的最高分”、“无论B做什么,A都能得到”。

刚开始我不理解,又看错了游戏规则(误以为B要让自己的分数最少),以为两个人会一个找最长可行路径、另一个会找最短可行路径。然后看清楚了游戏规则,想到枚举两个人轮流走的所有可能路径,取最高分,但想想觉得会超时。之后又在想直接算出理想最高分,想到动态规划、图论、二分……

实际上,这几句话的正确理解是,包含以下几层意思(觉得难懂可以直接看例子):

  • A推演了所有未来情况(A怎么走,B怎么走,A再怎么走,B再怎么走……)
  • 一定能得到的分数是指:A在考虑了B的所有选择(包括B用最优策略的情况),也就是“无论B做什么”,B玩得特别好也好,B玩得特别烂也罢,A总能达到的一个确定的分数(实际上是B表现的特别好时的分数)。
  • 一定能得到的最高分是指:通过最优策略使这个分数提到最高,的那个分数。
  • A能预知到自己每种走法能一定得到的得分,然后选择其中分数最高的走法,作为自己的最优策略。每一轮都如此,每一轮A都能预知到该轮自己的各种走法(通过模拟B的所有相应走法,再模拟自己的走法……)直到游戏结束时能一定得到的最高分,从而决定自己当前轮该怎么走。
  • 这些道理对B也是一样的。B也推演了未来所有情况,知道自己当下做哪种选择一定能得到的分数最低,会按照这个最优策略进行游戏。
  • 每轮A推演的情况中已包含了B所推演的情况,也就知道B下一步会做的选择,从而A会做出当前最优的选择。

简单说最优策略就是:

  • 当有多种选择,并预知每种选择的分值,选择分值最优的。
  • A的最优策略:推演未来所有情况,预判每种走法一定能得到的分数,选择分数最高的走法。
  • B的最优策略:推演未来所有情况,预判每种走法一定能得到的分数,选择分数最低的走法。
  • 要计算一定能得到的分数,需要递归地推演接下来的游戏过程,注意回溯时需要应用最佳策略。

再解释一下(可以不看):

  • 一定能得到的最高分不是未来所有情况中的最高分(只有B一心按最短的死路走,A才能得到这个分数),只要B认真玩,A就不可能。
  • 能一定得到的最高分不一定是实际的最后分数,实际分数仍取决于B怎么走,只是保证一定不比这个差。不过题目要求两个人都采取最优策略,A的走法和B的走法就全部固定了(游戏过程全部固定),最后的实际得分就是这个分数。
  • 就算A有先手优势,A的最优策略不代表A能逼着B做某种选择,B的选择权仍然在,B可以是个很好的玩家,做出很好的选择。A的最优策略只能(通过自己的选择)限制B的未来选择,比如从B的最好的4种未来中,让其中最好的2种变得不可能。

举例,在某轮中,在A的第一种走法选择下(如A向左走一步),预计到B的相应两种选择(如B向右走一步或向上走一步)所产生的一定得到的得分是6、12,A的该选择的保证一定得分就是6(意思是不管B做什么选择,A能保证得到6分,6也是B做最佳选择的情况,记住B要让分数最低)。
如果该轮A共有三种走法选择,每种选择的保证得分是6、-2、7,A的该轮保证最高得分就是7(A会选第三种,从而让分数最高)。

(例子中B的两种选择的保证最高得分是通过推演下一轮(及之后)A的选择算出的,要递归算)

为了推演未来所有的情况,算法就是枚举A和B的所有游戏状态,也就是DFS出两者的所有可能路径(交叉走)。

这题的解法关键是:推演所有情况+回溯时做出最优决策。

代码

核心伪代码:

state[pos]全局变量:表示每个房间的当前状态,free或者blocked,free表示可以进入,blocked表示不可进入(已粉刷或损坏)
dfs函数:求从当前游戏状态算起的保证最高得分,游戏状态用地图的状态(同上)、a的位置、b的位置三个变量来表示

dfs(apos, bpos):
	score = max{
		for each possible(not blocked) next position of A {
			state[nextapos] = blocked
			= min{
				for each possible next position of B {
					state[nextbpos] = blocked
					= dfs(nextapos, nextbpos)
					state[nextbpos] = free
				}
				else // B game over if impossible to move
				{
					= 1 + dfs(nextapos, b)
				}
			}
			state[nextapos] = free
		}
		else // A game over
		{
			= min{
				for each possible next position of B {
					state[nextbpos] = blocked
					= -1 + dfs(apos, nextbpos)
					state[nextbpos] = free
				}
				else // A,B game over
				{
					return 0
				}
			}
		}
	}

answer = dfs(astartpos, bstartpos)

实际上不会超时,得感谢出题人精确地控制了边长范围,三角形网格的结构也使得路径数不太多。

延伸:用类似方法判断抢30游戏是否有必胜策略

再来个经典抢30的案例,帮助加深下对博弈论题目的经典编程套路的理解。

抢30的规则大家都清楚,A、B轮流取1~3个数,先抢到30为胜。必胜策略大家应该也都知道,先手必胜(且第一轮需抢到2)。

现在我们要用程序判定A是否有先手必胜策略。为了增强问题普适性,不妨把目标30改为X,变成“抢X游戏”,但每轮每人依然最多只能3个数。

算法是:用f(i)表示上一轮B抢到i时,A是否有必胜策略(f(0)为初始状态,表示A是否有先手必胜策略)。

判定某状态有必胜策略的条件为以下之一:

  1. A做出某种选择可以直接抢到X;
  2. A做出某种选择时,无论B选择1到3中的哪个数,A接下来都能赢。

以上条件可表示为

  1. i+1 <= X <= i+3
  2. 对于某个nexta(即i+1i+2i+3),(f(nexta+1) and f(nexta+2) and f(nexta+3)) == true(表示B不管做出3种选择中的哪一种,A都能赢)。

边界条件是f(X)=false(表示B抢到了X,A输了),f(X+1)=f(X+2)=f(X+3)=true

程序结构与上文类似,也是DFS枚举两人的选择,很好写

def f(lastb):
    if lastb == X:
        return False
    elif lastb > X: # (1)
        return True

    # for each next position of A
    for nexta in [lastb+1, lastb+2, lastb+3]:
        if nexta == X: # (2)
            return True

        # for each next position of B
        if f(nexta+1) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值