Broken Necklace-----破碎的项链----USACO---C1S1

来源:互联网 发布:linux cat 查看日志 编辑:程序博客网 时间:2024/06/02 14:57

在USACO中Chapter1,Section1的题目。记录以便日后查看。翻译及部分思路来自NOCOW。

题目的中文翻译为:

你有一条由 N 个红色的,白色的,或蓝色的珠子组成的项链(3<=N<=350),珠子是随意安排的. 这里是 n=29 的二个例子: 

第一和第二个珠子在图片中已经被作记号. 
图片 A 中的项链可以用下面的字符串表示: 
brbrrrbbbrrrrrbrrbbrbbbbrrrrb . 
假如你要在一些点打破项链,展开成一条直线,然后从一端开始收集同颜色的珠子直到你遇到一个不同的颜色珠子,在另一端做同样的事.(颜色可能与在这之前收集的不同) 确定应该在哪里打破项链来收集到最大多数的数目的子. Example 举例来说,在图片 A 中的项链,可以收集到 8 个珠子,在珠子 9 和珠子 10 或珠子 24 和珠子 25 之间打断项链. 在一些项链中,包括白色的珠子如图片 B 所示. 当收集珠子的时候,一个被遇到的白色珠子可以被当做红色也可以被当做蓝色. 表现项链的字符串将会包括三符号 r , b 和 w . 写一
个程序来确定从一条被供应的项链最大可以被收集珠子数目. 

问题解答:

        可以把珠子想象成从第n个和第一个珠子之间断裂,此时项链变成了一个串,在此串上解决问题。

        算法一:搜索法

                从项链的第一个珠子算起,分别向两个方向搜索最长的连续字串,并记录最大值。算法的复杂度为O(N2)。

        算法二:动态规划法

                分别从两个方向搜索到项链的第i个珠子时,最长的字串。例如在第3个和第4个珠子之间断裂,先不考虑白色的珠子,则如下图:

                  可以看出,只要求出在第i个和第i+1个位置断裂时,截止到第i个珠子从右到左的最大字串长度和截止到第i+1个珠子时从左到有的最大字串长度即可。
                  先看从右到左时:
                   用一个数组保存最大字串的长度,记为R2L。则
                                                              R2L[i-1] + 1        第i-1个珠子和第i个珠子相同
                                      R2L[i] = 
                                                                        1                 第i-1个珠子和第i个珠子不相同    
                   有了此公式便可以用动态规划求出从右到左时的最大字串长度。但是此时还有一个问题,之前我们把项链看成是从第n个位置和第1个位置之间裂开的,但是实际并不是这样,项链是一个环。此时 我们可以用两种方法解决。

                   第一种:
                   将项链看成两个串相连,例如从第一个到第4个分别为RRBB。我们把他看成
                                                          array   =    RRBB   +     RRBB。     (i=1....n)
                    按动态规划公式求出最长字串。并将array[3] 对应到R2L[3],array[4]对应到R2L[4],array[5]对应到R2L[1],array[6]对应到R2L[2]。即去前串的后半部分,后串的前半部分。

                    第二种:
                    先判断是否都为一个颜色的串,若是一个颜色的串,则R2L[i] = n。否则计算R2L[1...n]后,再判断珠子的第1个和第n个是否相同,若相同,再计算R2L[1...i]直到第i-1个珠子和第i个珠子不相同为止。

                  从左到右时,同理
                                                         R2L[i+1] + 1        第i+1个珠子和第i个珠子相同
                                         L2R[i] = 
                                                                 1                    第i+1个珠子和第i个珠子不相同   
                   
    上面我们忽略了W珠子的存在,现在来考虑W珠子。由于W珠子可以当做蓝色或红色珠子,所以我们把W珠子按红色扫描一遍,再将W当做蓝色扫描一遍。并合并两次扫描的结果,取最大的结果。
                    
                     之后就是扫描R2L和L2R数组,假设在第i和i+1位置之间断开,则计算R2L[i] + L2R[i+1],求出所有的和,取最大的即为结果。


算法的实现:

package chapt1.section1;public class P4 {/** * 根据array填充R2L * @param array * @param a    R2L数组 * @param s    将W当做R或者B */public void fill(char[] array,int[] a,char s){int n = array.length;a[0] = 1;for(int i = 1 ; i < n ; i++){char ai = array[i]=='w' ? s : array[i];char ai1 = array[i-1] == 'w' ? s : array[i-1];if(ai == ai1)a[i] = a[i-1] + 1;elsea[i] = 1;}if(array[n-1] == 'w' || array[n-1] == s){a[0] = a[n-1] + 1;for(int i = 1; i < n ; i++){char ai = array[i]=='w' ? s : array[i];char ai1 = array[i-1] == 'w' ? s : array[i-1];if(ai == ai1)a[i] = a[i-1] + 1;elsebreak;}}}/** * 填充L2R * @param array * @param a    L2R 数组 * @param s    将W当做R或B        */public void fill2(char[] array,int[] a,char s){int n = array.length;a[n-1] = 1;for(int i = n-2 ; i >= 0 ; i--){char ai = array[i]=='w' ? s : array[i];char ai1 = array[i+1] == 'w' ? s : array[i+1];if(ai == ai1)a[i] = a[i+1] + 1;elsea[i] = 1;}if(array[0] == 'w' || array[0] == s){a[n-1] = a[0] + 1;for(int i = n-2 ; i >= 0 ; i--){char ai = array[i]=='w' ? s : array[i];char ai1 = array[i+1] == 'w' ? s : array[i+1];if(ai == ai1)a[i] = a[i+1] + 1;elsea[i] = 1;}}}/** * 取两个数组中最大的值放入a1中 * @param a1 * @param a2 */public void merge(int[] a1,int[] a2){for(int i = 0; i < a1.length ; i++){if(a2[i] > a1[i])a1[i] = a2[i];}}public void beads(char[] array){int n = array.length;int[] rcw = new int[n];   //R2L数组,将W当做R    cw为clockwiseint[] bcw = new int[n];   //R2L数组,将W当做Bint[] bccw = new int[n];  //L2R数组,将W当做R    ccw为counter clockwiseint[] rccw = new int[n];  //L2R数组,将W当做Bboolean flag[] = {false,false};//检查是否全部相同,即是否为RRRRRRRRRR或BBBBBB这样的串for(int i = 0; i < n ; i++){if(array[i] == 'r'){flag[0] = true;}if(array[i] == 'b'){flag[1] = true;}}if(flag[0] == false || flag[1] == false){//如果是,结果为nSystem.out.println(n);return;}else{//计算数组fill(array,rcw,'r');fill(array,bcw,'b');fill2(array,rccw,'r');fill2(array,bccw,'b');//合并merge(rcw,bcw);merge(rccw,bccw);//rcw   rccwint sum = -1;for(int i = 0 ; i < n ; i++){if(i == n-1){if(sum < (rcw[i] + rccw[0]))sum = (rcw[i] + rccw[0]);}else{if(sum < (rcw[i] + rccw[i+1]))sum = (rcw[i] + rccw[i+1]);}}System.out.println(sum);}}public static void main(String[] args){String b = "wwwbbrwrbrbrrbrbrwrwwrbwrwrrr";new P4().beads(b.toCharArray());}                                       }




 

0 0