P1203 [IOI 1993 / USACO1.1] 坏掉的项链 Broken Necklace

记录84

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;// 典型的序列决策
int leftR[800],leftB[800],rightR[800],rightB[800];
int main(){//从左右两头数红蓝两种状态,四种组合
	int n,ans=0;
	string s,s2;
	cin>>n>>s;
	s2=s+s;// 环状,加一个自己就能包括收尾相连的状态了
	int len=2*n;
	if(s2[0]=='r'||s2[0]=='w') leftR[0]=1;
	if(s2[0]=='b'||s2[0]=='w') leftB[0]=1;
	for(int i=1;i<len;i++){//左边从开头第二个数状态的情况
		if(s2[i]=='r'||s2[i]=='w') leftR[i]=leftR[i-1]+1;
		if(s2[i]=='b'||s2[i]=='w') leftB[i]=leftB[i-1]+1;
	}
	if(s2[len-1]=='r'||s2[len-1]=='w') rightR[len-1]=1;
	if(s2[len-1]=='b'||s2[len-1]=='w') rightB[len-1]=1;
	for(int i=len-2;i>=0;i--){//右边到数第二个数状态
		if(s2[i]=='r'||s2[i]=='w') rightR[i]=rightR[i+1]+1;
		if(s2[i]=='b'||s2[i]=='w') rightB[i]=rightB[i+1]+1;
	}
	for(int i=0;i<=len-2;i++){
	ans=max(ans,max(leftR[i],leftB[i])+max(rightR[i+1],rightB[i+1]));
	}//状态转移,每次取左边最大的跟右边最大的,然后将答案更新为最大
  if(ans>n) ans=n;//最大不会超珠子数
	cout<<ans;
	return 0;
} 

题目传送门https://www.luogu.com.cn/problem/P1203


思路

🎯 问题本质

  • 有一条环形项链 (首尾相连),由 n 颗珠子组成,颜色为 'r'(红)、'b'(蓝)、'w'(白)
  • 白色珠子可以当作任意颜色
  • 你可以在任意两个相邻珠子之间"打断",将环变成一条线
  • 然后从左端向右 收集同色珠子(直到遇到不同颜色),从右端向左也做同样操作
  • 目标:最大化总共能收集的珠子数

💡 关键点:

  • 打断位置决定左右两段
  • 左右可以是不同颜色(例如左边收红色,右边收蓝色)
  • 白色可灵活匹配

🧠 解题策略:破环成链 + 预处理左右最长同色段

1. 破环成链
  • 将原字符串 s 复制一份拼接:s2 = s + s
  • 这样任意长度为 n 的连续子串都对应环上的一种打断方式
2. 预处理四个数组

s2(长度 2n)预处理:

数组 含义
leftR[i] s2[0]s2[i]以红色(或白)结尾的最长连续非蓝段长度(即:若只允许收红/白,从左到 i 能收多少)
leftB[i] 类似,只允许收蓝/白
rightR[i] s2[i]s2[2n-1],向右收红/白的最大长度
rightB[i] 向右收蓝/白的最大长度

✅ 注意:因为白色可当任意色,所以:

  • 在"收红色"时,'w' 可以算作 'r'
  • 在"收蓝色"时,'w' 可以算作 'b'
3. 枚举所有打断位置
  • s2 中,打断位置对应于 在 i 和 i+1 之间断开i 从 0 到 2n-2
  • 左半部分:s2[0..i] → 最多可收 max(leftR[i], leftB[i])
  • 右半部分:s2[i+1..2n-1] → 最多可收 max(rightR[i+1], rightB[i+1])
  • 总数 = 左 + 右
4. 边界处理
  • 最多不能超过 n(总珠子数)

代码分析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int leftR[800], leftB[800], rightR[800], rightB[800];
  • 定义四个预处理数组(大小 800 > 2×350,安全)
cpp 复制代码
int main(){
    int n, ans = 0;
    string s, s2;
    cin >> n >> s;
    s2 = s + s; // 破环成链
    int len = 2 * n;

🔹 初始化 left 数组(从左往右)

cpp 复制代码
    if(s2[0] == 'r' || s2[0] == 'w') leftR[0] = 1;
    if(s2[0] == 'b' || s2[0] == 'w') leftB[0] = 1;
  • 第一个字符:
    • 如果是 'r''w',可以作为红色段开头 → leftR[0]=1
    • 如果是 'b''w',可以作为蓝色段开头 → leftB[0]=1
cpp 复制代码
    for(int i = 1; i < len; i++){
        if(s2[i] == 'r' || s2[i] == 'w')
            leftR[i] = leftR[i-1] + 1;
        if(s2[i] == 'b' || s2[i] == 'w')
            leftB[i] = leftB[i-1] + 1;
    }
  • 若当前是 'r'/'w',则红色段延续;否则 leftR[i] 保持 0(未显式清零,但全局初始化为 0)
  • 同理处理蓝色

⚠️ 注意:如果 s2[i] == 'b',则 leftR[i] 不更新(仍为 0),表示不能继续收红色


🔹 初始化 right 数组(从右往左)

cpp 复制代码
    if(s2[len-1] == 'r' || s2[len-1] == 'w') rightR[len-1] = 1;
    if(s2[len-1] == 'b' || s2[len-1] == 'w') rightB[len-1] = 1;
  • 最后一个字符初始化
cpp 复制代码
    for(int i = len - 2; i >= 0; i--){
        if(s2[i] == 'r' || s2[i] == 'w')
            rightR[i] = rightR[i+1] + 1;
        if(s2[i] == 'b' || s2[i] == 'w')
            rightB[i] = rightB[i+1] + 1;
    }
  • 从右往左累加,逻辑同 left

🔥 枚举所有打断位置

cpp 复制代码
    for(int i = 0; i <= len - 2; i++){
        ans = max(ans,
            max(leftR[i], leftB[i]) + max(rightR[i+1], rightB[i+1])
        );
    }
  • ii+1 之间打断
  • 左边 [0..i]:最多收 max(leftR[i], leftB[i])
  • 右边 [i+1..end]:最多收 max(rightR[i+1], rightB[i+1])
  • 更新全局最大值

🔚 边界修正

cpp 复制代码
    if(ans > n) ans = n;
    cout << ans;
    return 0;
}
  • 因为项链只有 n 颗珠子,不可能收集超过 n
  • 某些情况下(如全白),计算结果可能为 2n,需截断

✅ 例如:n=3, s="www"

  • leftR[2]=3, rightR[3..] 不存在,但在 s2="wwwwww" 中,i=2 时:
    leftR[2]=3, rightR[3]=3 → 总和 6,但实际最多只能拿 3 颗 → 修正为 n

总结

要点 说明
核心技巧 破环成链(s+s
状态设计 四个方向数组,分别记录红/蓝(含白)的最长连续段
白色处理 在红/蓝统计中都包含 'w'
枚举打断 遍历所有 i,取左右最大值之和
边界修正 结果不超过 n
相关推荐
_小草鱼_2 小时前
【搜索与图论】BFS(广度优先搜索)
算法·图论·bfs·宽度优先
weixin_421922692 小时前
分布式日志系统实现
开发语言·c++·算法
不想看见4042 小时前
Daily Temperatures单调栈--力扣101算法题解笔记
算法
炽烈小老头2 小时前
【每天学习一点算法 2026/03/21】颜色分类
学习·算法
qyzm2 小时前
天梯赛练习题
数据结构·python·算法·贪心算法
灰色小旋风2 小时前
力扣17 电话号码的字母组合(C++)
c++·算法·leetcode
无敌憨憨大王2 小时前
并查集(图论)
数据结构·算法·图论
xiaoye-duck2 小时前
《算法题讲解指南:动态规划算法--路径问题》--7.礼物的最大价值,8.下降路径最小和
c++·算法·动态规划
光影少年2 小时前
react的diff算法和vue的diff算法区别
vue.js·算法·react.js