Flipping Game
CF327A Flipping Game
小I有些无聊,所以他发明了一个在纸上玩的游戏。
他写下了n个整数a1,a2,a3,a4......an,每个都是0或1中的一个。他被允许做如下的一次操作:他选择一个起点i,一个终点j,保证1<=i<=j<=n,然后将区间中的每一个数翻转。翻转指将ax的值设定为1-ax。
问:翻转一次后,最多有几个1.
题目描述
Iahub got bored, so he invented a game to be played on paper.
He writes $ n $ integers $ a_{1},a_{2},...,a_{n} $ . Each of those integers can be either 0 or 1. He's allowed to do exactly one move: he chooses two indices $ i $ and $ j $ ( $ 1<=i<=j<=n $ ) and flips all values $ a_{k} $ for which their positions are in range $ [i,j] $ (that is $ i<=k<=j $ ). Flip the value of $ x $ means to apply operation $ x=1 $ - $ x $ .
The goal of the game is that after exactly one move to obtain the maximum number of ones. Write a program to solve the little game of Iahub.
输入格式
The first line of the input contains an integer $ n $ ( $ 1<=n<=100 $ ). In the second line of the input there are $ n $ integers: $ a_{1},a_{2},...,a_{n} $ . It is guaranteed that each of those $ n $ values is either 0 or 1.
输出格式
Print an integer — the maximal number of 1s that can be obtained after exactly one move.
输入输出样例 #1
输入 #1
5
1 0 0 1 0
输出 #1
4
输入输出样例 #2
输入 #2
4
1 0 0 1
输出 #2
4
说明/提示
In the first case, flip the segment from 2 to 5 $ (i=2,j=5) $ . That flip changes the sequence, it becomes: [1 1 1 0 1]. So, it contains four ones. There is no way to make the whole sequence equal to [1 1 1 1 1].
In the second case, flipping only the second and the third element $ (i=2,j=3) $ will turn all numbers into 1.
CF 327A — Flipping Game(思路笔记)
题意与目标
给定一个 0/1 数组,必须选择一个连续区间 [i, j] 把其中每个元素取反(0↔1),使得最终数组中的 1 的个数最大。输出最大值。
关键转化:把“翻转”变成“收益”
翻转某段时:
- 0 → 1:对答案的增量 +1
- 1 → 0:对答案的增量 −1
据此构造收益数组 b:
b[k] = +1,若a[k] = 0b[k] = −1,若a[k] = 1
记原数组 1 的个数为 ones。若翻转区间为 [i, j],这段带来的总收益就是 sum(b[i..j])。
所以答案为:
最后 answer = ones + best。
边界与特判
- 全是 1:因为必须翻一次,无论翻哪一段都会至少损失 1 个 1,最优是
n-1。
在收益数组里表现为b ≡ -1,最大子段和best = -1,代入ones + best = n - 1。 n = 1:[1]→ 翻后0,答案0[0]→ 翻后1,答案1
一般公式也能正确给出。
复杂度
- 时间:
O(n)(一次线性扫描) - 空间:
O(1)(Kadane)或O(1)额外空间(前缀和用滚动变量)
示例快走一遍
样例 1
a = [1, 0, 0, 1, 0],ones = 2
b = [-1, +1, +1, -1, +1]
最大子段和取 [2..5]:(+1)+(+1)+(-1)+(+1) = +2
answer = 2 + 2 = 4 ✅
样例 2
a = [1, 0, 0, 1],ones = 2
b = [-1, +1, +1, -1]
最大子段和取 [2..3]:+2
answer = 2 + 2 = 4 ✅
碰到“类似题”的通用套路
- 翻转一段 0/1,使某个计数最大
- 把翻转带来的增量写成逐点收益,0→+1、1→−1(或按目标权重设定),做最大子段和。
- 把一段取反/替换,使某种函数最优
- 对每个位置写出“操作后贡献 − 操作前贡献”作为收益,再做最大子段和。
- 允许“最多一次”操作
- 若最大子段和 ≤ 0,则不操作(与本题不同,本题“必须一次”)。
- 必须/最多进行
k次(k>1)- 变成“选
k个不相交子段收益最大”,用 DP(分段最大和,O(nk))解决。
- 变成“选
- 把最多 k 个 0 变 1,使连续 1 最长
- 属于滑动窗口:窗口内 0 的个数 ≤ k,维护最大长度。
常见坑 & 自检清单
- ✅ 一定要在收益数组
b上求最大子段和,而不是直接在a上。 - ✅ 记住本题是必须翻一次 → Kadane/前缀和求的是非空子段。
- ✅ 全 1 时返回
n-1(等价于ones + (-1))。 - ✅ 细节初始化正确:Kadane 的第一项,或前缀和的
min_pref与P[0]。 - ✅
n=1的极小样例符合预期。
/*
修正点:
1) 构造收益串 b:a[i]==0 -> +1,a[i]==1 -> -1
2) 在 b 上做最大子段和(Kadane/DP),并正确初始化 f[1]
3) ones==n 时必须翻一次 → 答案 n-1,直接返回,避免多次输出
4) 只输出一次:answer = ones + Max
*/
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, a[N];
int b[N]; // 收益串
int f[N]; // f[i]:以 i 结尾的最大子段和(非空)
int main() {
cin >> n;
int ones = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ones += a[i];
}
// 必须做一次翻转:全是 1 时最优只能变成 n-1
if (ones == n) {
cout << n - 1 << '\n';
return 0;
}
// 构造收益串:0 -> +1,1 -> -1
for (int i = 1; i <= n; i++) b[i] = (a[i] == 0 ? 1 : -1);
// 在 b 上做最大子段和(Kadane/DP 版),注意初始化 f[1]
f[1] = b[1];
int Max = f[1];
for (int i = 2; i <= n; i++) {
f[i] = max(b[i], f[i - 1] + b[i]);
Max = max(Max, f[i]);
}
// 此时至少有一个 0,Max >= 1
cout << (ones + Max) << '\n';
return 0;
}

浙公网安备 33010602011771号