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

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

题目的中文翻译为:

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

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

问题解答:

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

        算法一:搜索法

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

        算法二:动态规划法

                分别从两个方向搜索到项链的第i个珠子时,最长的字串。例如在第3个和第4个珠子之间断裂, 先不考虑白色的珠子,则如下图:
Broken Necklace-----破碎的项链----USACO---C1S1_第2张图片
                  可以看出,只要求出在第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;
			else
				a[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;
				else
					break;
			}
		}
		
	}
	
	/**
	 * 填充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;
			else
				a[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;
				else
					a[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为clockwise
		int[] bcw = new int[n];   //R2L数组,将W当做B
		int[] bccw = new int[n];  //L2R数组,将W当做R    ccw为counter clockwise
		int[] rccw = new int[n];  //L2R数组,将W当做B
		
		boolean 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){
			//如果是,结果为n
			System.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   rccw
			int 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());
	}
	                                       
}




 

你可能感兴趣的:(java,算法,动态规划,USACO)