USACO 2024 December Contest, SilverProblem 1. Cake Game (AcWing 6118. 蛋糕游戏)Python 题解
Bessie 和 Elsie 发现了一行 N N N 个蛋糕 ( 2 ≤ N ≤ 5 ⋅ 1 0 5 , N 为偶数) (2≤N≤5⋅10^5,N 为偶数) (2≤N≤5⋅105,N为偶数),大小依次为 a 1 , a 2 , … , a N ( 1 ≤ a i ≤ 1 0 9 ) a_1,a_2,…,a_N(1≤a_i≤10^9) a1,a2,…,aN(1≤ai≤109)。
两头奶牛都想吃到尽可能多的蛋糕。但是,作为非常文明的奶牛,她们决定玩一个游戏来分割蛋糕!游戏在两头奶牛之间轮流进行回合。每个回合进行以下两者之一:
Bessie 选择两个相邻的蛋糕并将它们堆叠起来,制造大小为两者大小之和的一个新蛋糕。
Elsie 选择最左边或最右边的蛋糕藏起来。
当只剩下一个蛋糕时,Bessie 吃掉它,而 Elsie 吃掉她藏起来的所有蛋糕。如果两头奶牛都采取最优策略以最大化她们吃到的蛋糕量,并且 Bessie 先进行回合,那么每头奶牛将会吃到多少蛋糕?
输入格式(从终端 / 标准输入读入):
每个测试点包含
T
(
1
≤
T
≤
10
)
T(1≤T≤10)
T(1≤T≤10)个独立的测试用例。输入保证一个测试点中的所有
N
N
N之和不超过
1
0
6
10^6
106。
每个测试用例的格式如下。
第一行包含
N
N
N
下一行包含
N
N
N个空格分隔的整数
a
1
,
a
2
,
…
,
a
N
a_1,a_2,…,a_N
a1,a2,…,aN。
输出格式(输出至终端 / 标准输出):
对于每个测试用例,输出一行,包含 b 和 e,表示 Bessie 和 Elsie 在两头奶牛都采取最优策略的情况下分别吃到的蛋糕量。
输入样例:
2
4
40 30 20 10
4
10 20 30 40
输出样例:
60 40
60 40
对于第一个测试用例,在最优策略下,
Bessie 将堆叠中间两个蛋糕。现在蛋糕的大小为
[
40
,
50
,
10
]
[40,50,10]
[40,50,10]。
Elsie 将吃掉最左边的蛋糕。现在剩余的蛋糕的大小为
[
50
,
10
]
[50,10]
[50,10]。
Bessie 堆叠剩余的两个蛋糕。
Bessie 将吃到
30
+
20
+
10
=
60
30+20+10=60
30+20+10=60的蛋糕,而 Elsie 将吃到
40
40
40的蛋糕。
第二个测试用例是第一个测试用例反转的情况,因此答案相同。
测试点性质:
测试点 2:所有
a
i
a_i
ai相等。
测试点 3:
N
≤
10
N≤10
N≤10。
测试点 4-7:
N
≤
5000
N≤5000
N≤5000。
测试点 8-11:没有额外限制。
题解
这题个人感觉挺抽象难理解的。不太好做说实话。我也是去看了闫老师的题解蓝桥杯集训·每日一题 | AcWing 6118. 蛋糕游戏。但是没太听懂,主要是听懂了A的选蛋糕策略。然后自己又理了一遍才比较明白。写一篇题解当复习笔记用了。
题目要点!
我一开始乍一眼没看懂,直接就去看了闫老师的题解。然后懵逼了。后面才后知后觉发现题目要点是什么:
表示 Bessie 和 Elsie 在两头奶牛都采取最优策略的情况下分别吃到的蛋糕量
最优策略!
这里把两头奶牛称为A和B,A奶牛是Bessie,B奶牛是Elsie。都采取最优策略。什么是最优策略。
显然B的最优策略非常好理解,就是每轮左右选最大!
那么A的最优策略呢?它的目标很好理解,就是吃最多!但是怎么选才能吃最多?
首先就是根据闫老师的方案,可以保证A必吃 n / / 2 + 1 n//2+1 n//2+1 个蛋糕。
然后我就证明了一下,如果A合并的蛋糕杯有被B吃了的,还有没有可能让A吃的蛋糕多?证明结果就是不行,A必须满满当当吃完 n / / 2 + 1 n//2+1 n//2+1个蛋糕才是最大的,但凡有一个合并的蛋糕被B吃了,最好情况也是少于某个吃满 n / / 2 + 1 n//2+1 n//2+1个蛋糕的情况。
非常好证明,假设B吃掉了一个合并蛋糕( a i , a i + 1 a_i,a_{i+1} ai,ai+1),B吃掉的是左边 0 ∼ i 0\sim i 0∼i(合并后 i , i + 1 → i i,i+1 \rightarrow i i,i+1→i),右边则是 t ∼ n t\sim n t∼n,那么就一定有一种方案是 a i , a i + 1 a_i,a_{i+1} ai,ai+1没被合并,然后A吃了 a i + 1 a_{i+1} ai+1,B依然吃的是左边 0 ∼ i 0\sim i 0∼i,右边 t ∼ n t\sim n t∼n的方案。
那么显然A吃掉 a i + 1 a_{i+1} ai+1显然是要比A没吃到 a i + 1 a_{i+1} ai+1蛋糕值要大的!
那么问题来了,既然A必然可以吃掉 n / / 2 + 1 n//2+1 n//2+1个蛋糕(按照闫总的方案),那么A的最优方案是什么?这个时候就是看A、B博弈了,显然在闫老师那个方案里,A是必然被动的,B才是主动的那个。那么A的最优方案的方案只能是A所有吃完 n / / 2 + 1 n//2+1 n//2+1 个蛋糕方案中最小的那个,因为主动的B显然会一直捡场面上左右两边最大的蛋糕来吃,而A如果此时不想让自己吃的少,那么只能让自己委屈了(但凡敢合并可以被B吃的蛋糕,那么必然会有前面证明的情况出现)!
然后就可以算A吃完 n / / 2 + 1 n//2+1 n//2+1个蛋糕方案中最小的那个出来,然后就得到了B的值
代码
t = int(input())
for i in range(t):
n = int(input())
cakes = list(map(int, input().strip().split()))
total = sum(cakes)
pre = [0 for _ in range(n+1)]
for i in range(1, n+1):
pre[i] = pre[i-1] + cakes[i-1]
win = float("INF")
for i in range(n//2+1, n+1):
win = min(win, pre[i]-pre[i-n//2-1])
print(win, total-win)
有没有可能用滑动窗口做这题?显然是可以滴。(求B的最大取值嘛)
t = int(input())
for i in range(t):
n = int(input())
cakes = list(map(int, input().strip().split()))
total = sum(cakes)
newcakes = cakes+cakes
len_win = n//2-1
newcakes = newcakes[n-len_win:n+len_win]
tmp, win = 0, 0
for idx, cake in enumerate(newcakes):
tmp += cake
if idx < len_win-1:
continue
win = max(win, tmp)
tmp -= newcakes[idx-len_win+1]
print(total-win, win)