1. 问题描述:
John 打算驾驶一辆汽车周游一个环形公路。公路上总共有 n 个车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
任务:判断以每个车站为起点能否按条件成功周游一周。
输入格式
第一行是一个整数 n,表示环形公路上的车站数;接下来 n 行,每行两个整数 pi,di,分别表示表示第 i 号车站的存油量和第 i 号车站到顺时针方向下一站的距离。
输出格式
输出共 n 行,如果从第 i 号车站出发,一直按顺时针(或逆时针)方向行驶,能够成功周游一圈,则在第 i 行输出 TAK,否则输出 NIE。
数据范围
3 ≤ n ≤ 10 ^ 6,
0 ≤ pi ≤ 2 × 10 ^ 9,
0 ≤ di ≤ 2 × 10 ^ 9
输入样例:
5
3 1
1 2
5 2
0 1
5 4
输出样例:
TAK
NIE
TAK
NIE
TAK
来源:https://www.acwing.com/problem/content/description/1090/
2. 思路分析:
3. 代码如下:
class Solution:
# 这道题目本质上是135题最大子序和的扩展, 这里求解的n段前缀和是否满足要求, 135题求解的是一段
def process(self):
n = int(input())
# oil[i]为第i个站点的油量, dis[i]为第i个站点到下一个站点的距离
oil, dis = [0], [0]
# 下标从1开始存储元素方便后面处理
for i in range(n):
a, b = map(int, input().split())
oil.append(a)
dis.append(b)
# 声明前缀和列表
s = [0] * (2 * n + 2)
# 顺时针求解每一个点到是否可以完整走一圈
for i in range(1, n + 1):
# 因为是复制一遍接在了原数组后面所以第i + n个位置与第i个位置的元素是一样的
s[i] = s[i + n] = oil[i] - dis[i]
# 累加前缀和, 前缀和存储的是这么多段的剩余油量
for i in range(1, 2 * n + 1):
s[i] += s[i - 1]
q = [0] * (2 * n + 2)
hh, tt = 0, 0
# q[0]为哨兵, 因为求解的是以当前站点i顺时钟是否可以绕一周所以可以逆时终计算长度为为n之内的前缀和的最小值判断是否所有长度在n之内的前缀和是否满足条件
q[0] = 2 * n + 1
res = [0] * (n + 1)
for i in range(2 * n, -1, -1):
# 区间长度大于了n说明hh头指针需要往后移动一位
if q[hh] > i + n: hh += 1
# 注意后面的下标是i + 1, 顺时钟做的时候不包含第i个站点的情况
if i < n:
if s[i] <= s[q[hh]]: res[i + 1] = 1
# 单调队列优化过程
while hh <= tt and s[q[tt]] >= s[i]: tt -= 1
tt += 1
q[tt] = i
# 逆时钟做一遍(其实与前面的过程是对称的), 注意第0个距离需要赋值为第n个站点的距离
dis[0] = dis[n]
# 处理前缀和
for i in range(1, n + 1):
# 注意与顺时钟做的时候的区别因为是逆时钟所以需要减去上一个元素的距离
s[i] = s[i + n] = oil[i] - dis[i - 1]
for i in range(1, 2 * n + 1):
s[i] += s[i - 1]
hh, tt = 0, 0
q[0] = 0
for i in range(1, 2 * n + 1):
if q[hh] + n < i: hh += 1
if i > n:
# 逆时钟做的时候包含第i个站点的情况
if s[i] >= s[q[hh]]: res[i - n] = 1
while hh <= tt and s[q[tt]] <= s[i]: tt -= 1
tt += 1
q[tt] = i
for i in range(1, n + 1):
# 只要有一个满足条件说明就是符合要求的
if res[i] == 1: print("TAK")
else: print("NIE")
if __name__ == "__main__":
Solution().process()