枚举每个断点,分别向左右按颜色和白珠规则模拟收集,并用终点位置避免两边重复计数。
OJ: luogu
题目 ID: P1203
难度:普及-
标签:模拟环形处理USACO
日期: 2026-05-31 16:58
题意
给出一条由 r、b、w 组成的环形项链。
你可以任选一个位置断开,然后从断口向左右两个方向收集珠子。
每个方向都只能继续收集:
- 白色珠子
w - 和当前认定颜色相同的珠子
问最多能收集多少颗。
思路
n 很小,直接枚举断点模拟就够了。
最直接的教学版写法如下:
cpp
// brute.cpp:枚举断点并直接按规则双向收集珠子,作为教学版和对拍基准程序。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 405;
int n;
char a[MAXN];
int next_idx(int x) {
x++;
if (x > n) {
x = 1;
}
return x;
}
int prev_idx(int x) {
x--;
if (x <= 0) {
x = n;
}
return x;
}
int collect_clockwise(int x, int &end_pos) {
char color = a[x];
int cnt = 1;
end_pos = x;
for (int p = next_idx(x); p != x; p = next_idx(p)) {
if (color == 'w' && a[p] != 'w') {
color = a[p];
}
if (a[p] == color || a[p] == 'w') {
cnt++;
end_pos = p;
}
else {
break;
}
}
return cnt;
}
int collect_counterclockwise(int x, int end_pos) {
if (x == end_pos) {
return 0;
}
char color = a[x];
int cnt = 1;
for (int p = prev_idx(x); p != x && p != end_pos; p = prev_idx(p)) {
if (color == 'w' && a[p] != 'w') {
color = a[p];
}
if (a[p] == color || a[p] == 'w') {
cnt++;
}
else {
break;
}
}
return cnt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
cin >> (a + 1);
int ans = 0;
for (int i = 1; i <= n; i++) {
int end_pos = 0;
int total = collect_clockwise(i, end_pos);
total += collect_counterclockwise(prev_idx(i), end_pos);
ans = max(ans, total);
}
if (ans > n) {
ans = n;
}
cout << ans << '\n';
return 0;
}对于每个断点:
- 从断口右边顺时针收集;
- 从断口左边逆时针收集;
- 两边数量相加更新答案。
实现时要注意三件事:
核心公式
枚举断点
最终答案为:
- 项链是环形,下标要能绕回;
- 白色
w可以当任意颜色; - 两边不能重复收集同一颗珠子,所以顺时针先记录终点,逆时针不能越过它。
公式解释:每个断点都独立计算一次左右两边最多能拿多少。两边相加可能超过整条项链长度,所以要和 n 取最小值;最后再对所有断点取最大值,就是最优断开位置。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 405;
int n;
char a[MAXN];
int next_idx(int x) {
x++;
if (x > n) {
x = 1;
}
return x;
}
int prev_idx(int x) {
x--;
if (x <= 0) {
x = n;
}
return x;
}
// 从 x 开始顺时针收集,并记录最后一个被收集到的位置。
int collect_clockwise(int x, int &end_pos) {
char color = a[x];
int cnt = 1;
end_pos = x;
for (int p = next_idx(x); p != x; p = next_idx(p)) {
if (color == 'w' && a[p] != 'w') {
color = a[p];
}
if (a[p] == color || a[p] == 'w') {
cnt++;
end_pos = p;
}
else {
break;
}
}
return cnt;
}
// 从 x 开始逆时针收集,但不能和顺时针部分重叠到 end_pos。
int collect_counterclockwise(int x, int end_pos) {
if (x == end_pos) {
return 0;
}
char color = a[x];
int cnt = 1;
for (int p = prev_idx(x); p != x && p != end_pos; p = prev_idx(p)) {
if (color == 'w' && a[p] != 'w') {
color = a[p];
}
if (a[p] == color || a[p] == 'w') {
cnt++;
}
else {
break;
}
}
return cnt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
cin >> (a + 1);
int ans = 0;
for (int i = 1; i <= n; i++) {
int end_pos = 0;
int total = collect_clockwise(i, end_pos);
total += collect_counterclockwise(prev_idx(i), end_pos);
ans = max(ans, total);
}
if (ans > n) {
ans = n;
}
cout << ans << '\n';
return 0;
}复杂度
- 时间复杂度:
- 空间复杂度:
总结
这题是典型的环形模拟题。
把环形下标、白珠规则和“不重复计数”这三件事处理好,代码就很稳定。
