你们好,欢迎阅读周末算法题专题。算法
今天选择的算法题来源于昨天同一套题中的D题,这题全场经过的人数在2600人左右。虽然经过的人数更少了一些,可是题目的难度却并无增长不少,可是趣味度增长了。我也是第一次碰见这样的问题。数组
题目连接:https://codeforces.com/contest/1405/problem/D数据结构
废话很少说,咱们一块儿来看这题的题意。app
咱们都知道数据结构当中的树有这样一个性质,若是树当中有n个点,那么它应该由n-1条无向边组成。而且树当中是必定没有环的,若是有环的话n-1条边就不够了。ide
今天是说有两我的分别叫作Alice和Bob在一棵树上玩游戏,这两我的名是业内的惯例。凡是两我的玩游戏的题目,主人公的名字不少都叫Alice和Bob,我也不知道这个惯例的由来,你们知道这么回事就行了。Alice和Bob两人各自占据了树上的一个点,而后两我的交替移动。Alice先手,Bob后手。测试
Alice每一次移动最多能够移动da距离,Bob最多能够移动db距离,两我的也能够放弃移动。假设两人都绝顶聪明每一次都会选择最佳策略,请问通过最多个回合以后,Alice可否捉到Bob呢?若是能够则Alice获胜,不然Bob获胜。注意在Bob的回合,它能够通过Alice的节点,这并不会被视为捉到。ui
不知道为何看到这道题的时候,老是会想起费玉清的段子,不知道大家有没有这种感受。url
第一行输入一个数t,表示测试数据的组数。spa
对于每组数据首先有5个数,分别是n, a, b, da, db。n表示树节点的个数,a和b表示游戏开始时a和b出生点的位置。da和db表示Alice和Bob每回合最多移动的距离。这几个数的最大范围都是,而且多组数据当中全部的n相加不超过。code
咱们来看两组样例:
4 3 2 1 2
1 2
1 3
1 4
6 6 1 2 5
1 2
6 5
2 3
3 4
4 5
第一组数据是这样的:
蓝色表示Alice,红色是Bob。因为Alice先手,只要Alice移动到1节点,不管Bob如何移动,他都必输无疑。
第二组数据:
因为Alice最多只能移动两格,第一回合移动到3,Bob选择不动。只要Alice移动,不管是一格仍是两格,Bob均可以直接移动到1节点。也就是说最终Bob就在1和6节点之间来回移动,躲开Alice的追捕。
看到Alice和Bob两人游戏,而且两人都绝顶聪明会选择最佳策略,首先想到的就是博弈论。可是观察一下你会发现这题和博弈论没有什么关系,由于博弈论每每都是公平博弈,局面会有状态转移。但这题当中不是,这题不存在胜负状态转移,必定会有一个肯定的结果,就是是谁胜谁负,因此这题不知足博弈论的前提。
相似博弈论的题面只是障眼法而已,若是你信了,而且真的往这方面努力,那么你就被出题人骗了。不过这个陷阱仍是比较明显的,很容易看出来。接下来咱们又遇到了另一个陷阱,这个陷阱也很明显,就是执行的回合数量。题目给的是,这个数是真正的天文数字,比宇宙当中全部的粒子数都要多。因此咱们能够彻底能够理解成无限游戏。
若是出题人阴险一点,彻底能够给一个这个量级的回合数,会更有欺骗性一些。其实咱们想一下就会发现,树上节点最多只有个,当它们玩追逐游戏的时候,只会有两种状况,一种是永远也追不上,还有一种是很快就追上。因此这个回合数没什么意义,就是唬人的。
首先,第一个洞见是这道题咱们使用模拟是不可行的。所谓的模拟也就是模拟题意的运行状况,去一步一步地分析每个玩家的选择,作出最好的决策,最后得出游戏的结果。不可行的缘由也很简单,由于会超时。这个很是明显,就不深刻解释了。
除此以外,咱们还能够获得其余一些洞见。首先第一个很简单的洞见是,若是Alice和Bob出生的位置相距小于da的话,那么Alice必胜。这个也很好理解,一开始的距离就在Alice的移动范围内,那Bob一上来就被抓住了。没有讨论的余地。另一个洞见是若是da >= db,那么Alice必胜。
也就是若是Bob跑得比Alice慢的话,他也同样必败。这也很简单,由于地图的范围是有限的,一个追一个逃,逃的速度比追的慢,显然他逃不出去。这个结论虽然简单,可是反过来一想会获得一个问题。若是Bob跑得比Alice快的话,他必定能够获胜吗?
咱们看第一个案例就知道这个答案不必定,由于地形也会影响最终的结果。树意味着每个节点都全连通,也意味着每两点的路线只有一条。换句话说每一条路线都是思路,即便Bob跑得快若是不反向跑甩掉Alice的话,那么他也是同样会被Alice追上的。
因此咱们接下来要作的就是深刻分析这里的状况,把剩下的问题捋清楚。
codeforces的问题有一个特色就是咱们必定要深刻分析样例,由于不少出题人很良心,给出的样例都是关键样例。咱们能够借助样例的状况帮助咱们分析问题。好比今天说的这题就是一个。
咱们来分析一下第一个样例:
这就是典型的Bob跑得比Alice快的样例,Bob不只跑得比Alice快,甚至仍是Alice的两倍。可是咱们都知道结果仍是Alice获胜,缘由也很简单,Alice移动到1号节点以后,Bob只有两个选择要么1号节点要么4号节点,这两个节点都距离1号节点只有一个距离,仍然在Alice追上的范围内。
在这个样例当中Bob的逃跑空间勉强是够的,但仍是被追上了,说明他的逃跑速度是不够的。不够的缘由也很简单,由于一开始当Alice移动到1节点以后,他距离Bob的距离是1,也就是一个da。这时Bob无路可走只能折返甩开Alice,这时候他最多可以走一个db,他要想不被Alice捉住,首先他须要先走过da这一段距离,接着他须要走出至少da+1的距离,才能保证Alice折返追他也追不到。那么咱们能够得出一个条件是db > 2da。
但咱们发现仅仅db > 2da仍是不够的,由于在这个例子当中咱们能够看得出来不够开阔,即便db=3,Bob也没处可去。也就是说在这棵树上至少有一条链路它的长度要大于2da才行,不然即便Bob再能跑也会被地形限制住。对于一棵树而言,求它的最长链路仍是比较简单的,咱们也在以前的文章当中讲解过,其实就是对于每个节点都求一个到叶子节点的最长距离和次长距离之和。全部的节点的距离最大的那个就是整棵树上的最长链路。
咱们整理一下思路,一共发现了3个条件,只有知足这3个条件,Bob才可能跑掉,不然必定会被Alice捉住。这三个条件分别是:
因此咱们要求的就只有两点,第一点是一开始它们之间的距离,以及树的直径(树上最长的链路长度)。好在这两点均可以经过递归实现。都理出来了以后代码就不难写了:
t = int(input())
depth = [0 for _ in range(200050)]
for _ in range(t):
n, a, b, da, db = list(map(int, input().split(' ')))
edges = [[] for _ in range(n+2)]
for i in range(n-1):
u, v = list(map(int, input().split(' ')))
edges[u].append(v)
edges[v].append(u)
diameter = 0
depth[a] = 0
def dfs(u, f):
global diameter
l = 0
for v in edges[u]:
if v == f:
continue
# depth数组记录每一个节点的树深
depth[v] = depth[u] + 1
cur = 1 + dfs(v, u)
# cur + l即u节点到叶子节点的最长距离和次长距离
# 直径就是这二者和之中最大的一个
diameter = max(diameter, cur + l)
l = max(l, cur)
return l
# 以Alice所在的点做为树根,这样depth[b]即Alice和Bob的距离
dfs(a, -1)
if depth[b] <= da or da * 2 >= diameter or da*2 >= db:
print('Alice')
else:
print('Bob')
咱们把整个思路说穿了是否是有一种一文不值的感受?可是本身思考要想明白仍是不太容易的,codeforces的问题就是这样,常常须要咱们在纸上画一画看一看。有时候一些点靠本身想很难彻底想明白,可是找一个例子试一试一下就清楚了。这也是codeforces问题有趣的地方之一。