Broken Necklace

Broken Necklace

You have a necklace of N red, white, or blue beads (3<=N<=350) some of which are red, others blue, and others white, arranged at random. Here are two examples for n=29:

                1 2                               1 2
            r b b r                           b r r b
          r         b                       b         b
         r           r                     b           r
        r             r                   w             r
       b               r                 w               w
      b                 b               r                 r
      b                 b               b                 b
      b                 b               r                 b
       r               r                 b               r
        b             r                   r             r
         b           r                     r           r
           r       r                         r       b
             r b r                             r r w
            Figure A                         Figure B
                        r red bead
                        b blue bead
                        w white bead

The beads considered first and second in the text that follows have been marked in the picture.

The configuration in Figure A may be represented as a string of b's and r's, where b represents a blue bead and r represents a red one, as follows: brbrrrbbbrrrrrbrrbbrbbbbrrrrb .

Suppose you are to break the necklace at some point, lay it out straight, and then collect beads of the same color from one end until you reach a bead of a different color, and do the same for the other end (which might not be of the same color as the beads collected before this).

Determine the point where the necklace should be broken so that the most number of beads can be collected.

Example

For example, for the necklace in Figure A, 8 beads can be collected, with the breaking point either between bead 9 and bead 10 or else between bead 24 and bead 25.

In some necklaces, white beads had been included as shown in Figure B above. When collecting beads, a white bead that is encountered may be treated as either red or blue and then painted with the desired color. The string that represents this configuration will include the three symbols r, b and w.

Write a program to determine the largest number of beads that can be collected from a supplied necklace.

PROGRAM NAME: beads

INPUT FORMAT

Line 1:N, the number of beads
Line 2:a string of N characters, each of which is r, b, or w

SAMPLE INPUT (file beads.in)

29
wwwbbrwrbrbrrbrbrwrwwrbwrwrrb

OUTPUT FORMAT

A single line containing the maximum of number of beads that can be collected from the supplied necklace.

SAMPLE OUTPUT (file beads.out)

11

OUTPUT EXPLANATION

Consider two copies of the beads (kind of like being able to runaround the ends). The string of 11 is marked.
                       original 'split'
                             v
wwwbbrwrbrbrrbrbrwrwwrbwrwrrb|wwwbbrwrbrbrrbrbrwrwwrbwrwrrb
                       ******|*****
                       rrrrrb|bbbbb  <-- assignments
                       5 x r  6 x b  <-- 11 total
分析:
	先找br或rb或rw……wb或bw……wr。例如:找到了bw……wr那么先数出b、r之间的w的个数,再从r开始往后数直到遇到b,然后从b往前数知道遇到r。注意从r还是往后数过的不要与从b往前数过的重复。最后三个数相加便得到了一种情况。
代码:
/*
 ID: jszhais1
 PROG: beads
 LANG: C++
 */
#include <fstream>
using namespace std;
char nextChar(int point,int &nextPoint,char *beads,int &length){
    return point+1<length?beads[nextPoint=point+1]:beads[nextPoint=0];
}
char preChar(int point,int &prePoint,char *beads,int &length){
    return point==0?beads[prePoint=length-1]:beads[prePoint=point-1];
}
int main(int argc, const char * argv[])
{
    ofstream fout("beads.out");
    ifstream fin("beads.in");
    char beads[351];int length=0,MAX=0;bool isin=false;
    while(fin>>length){
        fin>>beads;
        for(int point=0 ; point<length ; point++)
        {
            int nextPoint=0,endP=-1, midCounter=0;
            char next=nextChar(point,nextPoint,beads,length);
            if(beads[point]!='w')
            {
                for(;next=='w';next=nextChar(nextPoint,nextPoint,beads,length), midCounter++);
                if(beads[point]!=next)
                {
                    isin=true;
                    int max=1+midCounter,wnextPoint=0;
                    char wnext=nextChar(nextPoint,wnextPoint,beads,length);
                    for(;(wnext=='w'||wnext==next)&&wnext!=endP;max++,wnext=nextChar(wnextPoint,wnextPoint,beads,length));
                    endP=(nextPoint+max-1)%length;
                    int wprePoint=0;
                    char wlast=preChar(point, wprePoint, beads, length);
                    for(max++;(wlast=='w'||wlast==beads[point])&&wprePoint!=endP;max++,wlast=preChar(wprePoint, wprePoint, beads, length));
                    endP=0;
                    if (max>MAX) {
                        MAX=max;
                    }
                }
            }
        }
        if(!isin)
            MAX=length;
        fout<<MAX<<endl;MAX=0;isin=false;
    }
    return 0;
}
答案:
Analysis: Broken Necklace Russ Cox

In this problem, the necklace size is small enough (350) that we might as well just try breaking the necklace at each point and see how many beads can be collected. This will take approximately O(n^2) time, but n is small enough that it won't matter.

The code is slightly simple-minded in that we might count the same beads twice if they can be taken off either side of the break. This can only happen if all beads can be taken off the necklace, so we just check for that at the end.

#include <stdio.h>
#include <string.h>
#include <assert.h>

#define MAXN 400

char necklace[MAXN];
int len;

/* 
 * Return n mod m.  The C % operator is not enough because
 * its behavior is undefined on negative numbers.
 */
int 
mod(int n, int m)
{
    while(n < 0)
	n += m;
    return n%m;
}

/*
 * Calculate number of beads gotten by breaking
 * before character p and going in direction dir,
 * which is 1 for forward and -1 for backward.
 */
int
nbreak(int p, int dir)
{
    char color;
    int i, n;

    color = 'w';

    /* Start at p if going forward, bead before if going backward */
    if(dir > 0)
	i = p;
    else
	i = mod(p-1, len);

    /* We use "n<len" to cut off loops that go around the whole necklace */
    for(n=0; n<len; n++, i=mod(i+dir, len)) {
	/* record which color we're going to collect */
	if(color == 'w' && necklace[i] != 'w')
	    color = necklace[i];

	/* 
	 * If we've chosen a color and see a bead
	 * not white and not that color, stop 
	 */
	if(color != 'w' && necklace[i] != 'w' && necklace[i] != color)
	    break;
    }
    return n;
}

void
main(void)
{
    FILE *fin, *fout;
    int i, n, m;

    fin = fopen("beads.in", "r");
    fout = fopen("beads.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%d %s", &len, necklace);
    assert(strlen(necklace) == len);

    m = 0;
    for(i=0; i<len; i++) {
	n = nbreak(i, 1) + nbreak(i, -1);
	if(n > m)
	    m = n;
    }

    /*
     * If the whole necklace can be gotten with a good
     * break, we'll sometimes count beads more than 
     * once.  this can only happen when the whole necklace
     * can be taken, when beads that can be grabbed from
     * the right of the break can also be grabbed from the left.
     */
    if(m > len)
	m = len;

    fprintf(fout, "%d\n", m);
    exit (0);
}

Daniel Bundala had an O(n) solution:

Dynamic Programming is good method for solving this problem in O(N). If we consider two copies of the string we easy transform cyclic configuration of the necklace to linear. Now we can compute for each breaking point how many beads of the same color can be collected on the left and on the right from the breaking point. I show how we can compute it only for the left side. For right side it is analogical. Let r[p] and b[p] be the number of red / blue beads that can be collected, when necklace is broken in point p. If we know this and color of next bead (c) we can compute r[p+1] and b[p+1].
 r[0] = p[0] = 0
 If c = 'r' then r[p+1] = r[p] + 1 and b[p+1] = 0
        because the length of the blue beads is 0.
 if c = 'b' then b[p+1] = b[p] + 1 and r[p+1] = 0
 if c = 'w' then both length of the red and length of blue beads
             can be longer.
so r[p+1] = r[p]+1 and b[p+1] = b[p] + 1.
The number of beads that can be collected in breaking point p is then max(left[r[p]], left[b[p]]) + max(right[r[p]], right[b[p]]). And the maximum from this value is answer for the problem.
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

FILE *in,*out;

int main () {
   in = fopen("beads.in", "r");
   out = fopen ("beads.out", "w");

   int n;
   char tmp[400], s[800];
   fscanf(in, "%d %s", &n, tmp);

   strcpy(s, tmp);
   strcat(s, tmp);

   int left[800][2], right[800][2];
   left[0][0] = left[0][1] = 0;

   for (int i=1; i<= 2 * n; i++){
       if (s[i - 1] == 'r'){
           left[i][0] = left[i - 1][0] + 1;
           left[i][1] = 0;
       } else if (s[i - 1] == 'b'){
           left[i][1] = left[i - 1][1] + 1;
           left[i][0] = 0;
       } else {
           left[i][0] = left[i - 1][0] + 1;
           left[i][1] = left[i - 1][1] + 1;
       }
     }

   right[2 * n][0] = right[2 * n][1] = 0;
   for (int i=2 * n - 1; i >= 0; i--){
       if (s[i] == 'r'){
           right[i][0] = right[i + 1][0] + 1;
           right[i][1] = 0;
       } else if (s[i] == 'b'){
           right[i][1] = right[i + 1][1] + 1;
           right[i][0] = 0;
       } else {
           right[i][0] = right[i + 1][0] + 1;
           right[i][1] = right[i + 1][1] + 1;
       }
   }

   int m = 0;
   for (int i=0; i<2 * n; i++)
       m = max(m, max(left[i][0], left[i][1]) + max(right[i][0], right[i][1]));
   m = min(m, n);
   fprintf(out, "%d\n", m);
   fclose(in); fclose(out);
   return 0;
}

Holland's Frank Takes has a potentially easier solution:

/* This solution simply changes the string s into ss, then for every starting
// symbol it checks if it can make a sequence simply by repeatedly checking 
// if a sequence can be found that is longer than the current maximum one.
*/

#include <iostream>
#include <fstream>
using namespace std;

int main() {
  fstream input, output;
  string inputFilename = "beads.in", outputFilename = "beads.out";  
  input.open(inputFilename.c_str(), ios::in);
  output.open(outputFilename.c_str(), ios::out);
  
  int n, max=0, current, state, i, j;
  string s;
  char c;
  
  input >> n >> s;
  s = s+s;
  for(i=0; i<n; i++) {
    c = (char) s[i];
    if(c == 'w')
      state = 0;
    else
      state = 1;
    j = i;
    current = 0;
    while(state <= 2) { 
      // dont go further in second string than starting position in first string
      while(j<n+i && (s[j] == c || s[j] == 'w')) { 
        current++;
        j++;
      } // while
      state++;
      c = s[j];
    } // while
    if(current > max)
      max = current;
  } // for
    
  output << max << endl;
  return 0;
} // main



结果:
	
USER: jim zhai [jszhais1]
TASK: beads
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 3360 KB]
   Test 2: TEST OK [0.000 secs, 3360 KB]
   Test 3: TEST OK [0.000 secs, 3360 KB]
   Test 4: TEST OK [0.000 secs, 3360 KB]
   Test 5: TEST OK [0.000 secs, 3360 KB]
   Test 6: TEST OK [0.000 secs, 3360 KB]
   Test 7: TEST OK [0.000 secs, 3360 KB]
   Test 8: TEST OK [0.000 secs, 3360 KB]
   Test 9: TEST OK [0.000 secs, 3360 KB]

All tests OK.

Your program ('beads') produced all correct answers! This is your submission #8 for this problem. Congratulations!

Here are the test data inputs:

------- test 1 ----
29
wwwbbrwrbrbrrbrbrwrwwrbwrwrrb
------- test 2 ----
3
rrr
------- test 3 ----
77
rwrwrwrwrwrwrwrwrwrwrwrwbwrwbwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwr
------- test 4 ----
17
wwwwwwwwwwwwwwwww
------- test 5 ----
50
bbrrrbrrrrrrrrbrbbbrbrrbrrrrbbbrbrbbbbbrbrrrbbrbbb
------- test 6 ----
8
rrwwwwbb
------- test 7 ----
200
rrrrrrrrrrrrrrrrrrrrbbbbbbbbbbbbbbbbbbbbrrrrrrrrrrrrrrrrrrrrbbbbbbbbbbbbbbbbbbbbrrrrrrrrrrrrrrrrrrrrbbbbbbbbbbbbbbbbbbbbrrrrrrrrrrrrrrrrrrrrbbbbbbbbbbbbbbbbbbbbrrrrrrrrrrrrrrrrrrrrbbbbbbbbbbbbbbbbbbbb
------- test 8 ----
350
rrbbrbbbwbwwbwbbbbwwrrbbwbrwbrwbbbrbrwrwbrwwwrrbbrrwrbbrwbwrwwwrbrwwwwwrwbwwwrrbrrbbbrbrbbbrbbbrbbwbbbbbrbrrbrwwbrrrrwbwrwrbbwbwrbrbrwwbrrbwbrwwbwwwbrbwrwbwbrbbbwrbwwrrrbwbwbbbbbrrwwwrbrwwrbbwrbbrbbrbwrrwwbrrrbrwbrwwrbwbwrrrbwrwrrbrbbwrwrbrwwwrwbwrrwwwwrrrwrrwbbwrwwrwrbwwbwrrrrbbwrbbrbwwwwwbrbbrbbrbrwbbwbwwbbbbbwwwrbwwbbbwrwwbbrrwrwbwrrwwwrrrwrrwww
------- test 9 ----
333
rwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwbrwb
Keep up the good work!
Thanks for your submission!
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #include<map> using namespace std; const int N=2005; const int inf=0x3f3f3f3f; int ans=-1; string a; int f(int x){ int s=0,i; char a1=a[x],b1=a[x+1]; for(i=x;;i--){ if(a[i]==a1||a[i]=='w') s++; else break; } for(i=x+1;;i++){ if(a[i]==b1||a[i]=='w') s++; else break; } return s; } int main() { int n,len,m,i,j,k,x,y; cin>>len>>a; a=a+a+a; for(i=len;i<len*2;i++){ if(a[i]==a[i+1]) continue; if(a[i]=='w'){ a[i]='r'; ans=max(ans,f(i)); a[i]='b'; ans=max(ans,f(i)); a[i]='w'; } ans=max(ans,f(i)); } ans=min(ans,len); if(ans==-1) ans=len; printf("%d",ans); return 0; } P1203 [USACO1.1]坏掉的项链Broken Necklace 题目描述 你有一条由 nn 个红色的,白色的,或蓝色的珠子组成的项链,珠子是随意安排的。 这里是 n=29n=29 的两个例子: 第一和第二个珠子在图片中已经被作记号。 图片 A 中的项链可以用下面的字符串表示: brbrrrbbbrrrrrbrrbbrbbbbrrrrb 假如你要在一些点打破项链,展开成一条直线,然后从一端开始收集同颜色的珠子直到你遇到一个不同的颜色珠子,在另一端做同样的事(颜色可能与在这之前收集的不同)。 确定应该在哪里打破项链来收集到最大数目的珠子。 例如,在图片 A 中的项链中,在珠子 99 和珠子 1010 或珠子 2424 和珠子 2525 之间打断项链可以收集到 88 个珠子。 白色珠子什么意思? 在一些项链中还包括白色的珠子(如图片B) 所示。 当收集珠子的时候,一个被遇到的白色珠子可以被当做红色也可以被当做蓝色。 表现含有白珠项链的字符串将会包括三个符号 r,b,w 。 写一个程序来确定从一条被给出的项链可以收集到的珠子最大数目。 输入格式 第一行一个正整数 nn ,表示珠子数目。 第二行一串长度为 nn 的字符串, 每个字符是 r , b 或 w。 输出格式 输出一行一个整数,表示从给出的项链中可以收集到的珠子的最大数量。 输入输出样例 输入 #1复制 29 wwwbbrwrbrbrrbrbrwrwwrbwrwrrb 输出 #1复制 11 说明/提示 【数据范围】 对于 100%100% 的数据,3\le n \le 3503≤n≤350 环的问题是把环复制一个或两个成链解决。 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.youkuaiyun.com/qq_20087731/article/details/104898471解释一下
最新发布
03-21
<think>好的,用户让我解释一个解决USACO Broken Necklace问题的C++代码。首先,我需要理解这个问题本身。题目是关于在一个环形项链上找到一个断点,使得从断点两端收集的珠子最多。珠子颜色有红(r)、蓝(b)和白(w),其中白色可以被当作红或蓝。 用户提供的代码看起来是处理这个问题的典型方法。首先,我需要通读代码,理解各个部分的作用。代码的结构包括头文件、常量和变量的声明,一个函数f(int x),主函数处理输入,将字符串重复三次,然后循环处理每个可能的断点,最后输出结果。 接下来,我需要拆解代码的各个部分。函数f的作用是计算在位置x和x+1之间断开时,能收集到的珠子数目。这里,函数先向左查找相同颜色或白色珠子,然后向右查找,可能包括处理白色珠子的不同可能性。主函数中,将原字符串重复三次,这是处理环形问题的常用方法,通过复制字符串来模拟环状结构,避免处理复杂的环边界条件。 然后,用户的问题中特别提到了白色珠子的处理方式,代码中的部分逻辑可能涉及到将白色珠子尝试替换成红或蓝,以寻找最优解。需要解释为什么这样做是正确的,以及如何确保覆盖所有可能的替换情况。 另外,代码中的一些细节需要注意,比如ans的初始值为-1,最后处理ans的方式,以及如何处理当所有珠子颜色相同的情况。例如,当ans仍然是-1时,将其设为len,这可能是因为所有珠子颜色相同,无法找到断点,所以整个项链都可以收集。 可能存在的疑问点包括:为什么要将字符串复制三次?为什么在循环中处理的是len到2*len的位置?如何确保覆盖所有可能的断点位置?这些都需要在解释中详细说明。 此外,用户之前的问题是关于a = a + a + a; 的代码,而当前代码中也有类似的操作,将a重复三次。可能需要联系之前的解释,说明这里复制三次是为了处理环形结构,使得在展开后的字符串中可以找到所有可能的断点而不必担心越界。 最后,需要确保解释符合用户的要求:结构清晰,步骤分明,使用中文,正确使用LaTeX格式,并且解释代码的逻辑、算法思路以及如何解决问题。同时,需要指出可能的优化点或代码中的注意事项,比如时间复杂度或特殊情况的处理。</think>让我们逐步分析这个解决**坏掉的项链问题**的C++代码实现: --- ### **算法核心思路** **环形处理技巧**:将原字符串复制三次拼接(`a = a + a + a`),使得环形遍历可以转化为线性遍历,避免处理环首尾相连的复杂边界条件。例如原长度为5时,拼接后`[5..10]`区域对应原环的所有可能展开方式。 --- ### **代码结构解析** #### **1. 预处理阶段** ```cpp a = a + a + a; // 三倍展开环形字符串 for(i = len; i < len*2; i++){ // 仅检查中间区域 // ... 核心处理逻辑 } ``` - **为什么三倍复制**:保证展开后的字符串中间部分(`[len, 2*len]`)包含所有可能的断点位置 #### **2. 关键函数`f(x)`** ```cpp int f(int x) { int s = 0; char a1 = a[x], b1 = a[x+1]; // 向左扫描 for(i = x;;i--) { if(a[i]==a1 || a[i]=='w') s++; else break; } // 向右扫描 for(i = x+1;;i++) { if(a[i]==b1 || a[i]=='w') s++; else break; } return s; } ``` - **功能**:计算在位置`x`与`x+1`之间打断时能收集的珠子数 - **扫描规则**: - 左端颜色固定为`a[x]` - 右端颜色固定为`a[x+1]` - `w`可作为任意颜色 #### **3. 白色珠子的特殊处理** ```cpp if(a[i] == 'w') { a[i] = 'r'; ans = max(ans, f(i)); // 尝试视为红色 a[i] = 'b'; ans = max(ans, f(i)); // 尝试视为蓝色 a[i] = 'w'; // 恢复原值 } ``` - **核心逻辑**:当遇到白色珠子时,分别尝试将其视为红色和蓝色,取最大值 #### **4. 边界条件处理** ```cpp ans = min(ans, len); // 防止超过实际长度 if(ans == -1) ans = len; // 全同色情况 ``` - **全同色保护**:当所有珠子颜色相同时,`f(x)`无法触发更新`ans` --- ### **算法流程图** $$ \begin{array}{|c|c|} \hline \text{输入字符串} & \rightarrow \text{三倍展开} \\ \downarrow & \\ \text{遍历中间段每个断点} & \rightarrow \text{处理白色歧义} \\ \downarrow & \\ \text{计算左右可收集长度} & \rightarrow \text{更新最大值} \\ \downarrow & \\ \text{边界修正} & \rightarrow \text{输出结果} \\ \hline \end{array} $$ --- ### **时间复杂度分析** - **主要开销**:对每个断点进行两次线性扫描 - **总复杂度**:$O(n^2)$,其中$n$为珠子数量 - **优化空间**:可通过预处理颜色段信息优化到$O(n)$ --- ### **测试样例解析** 以样例输入`wwwbbrwrbrbrrbrbrwrwwrbwrwrrb`为例: - **最佳断点**:将部分`w`视为红色后,可收集到11个珠子 - **计算过程**: - 左端连续`w`视为红色 - 右端连续`r`和`w`的组合 --- ### **关键优化点说明** - **三倍复制技巧**:避免处理环形索引的模运算 - **白色动态替换**:通过临时修改字符值穷举可能性 - **扫描终止条件**:遇到不同颜色立即停止 该解法充分利用了暴力法的直观性,同时通过字符串复制技巧降低了环形处理的复杂度。对于题目给定的数据范围(n ≤ 350),$O(n^2)$复杂度是可接受的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值