三色旗问题

参考:http://caterpillar.onlyfun.net/Gossip/AlgorithmGossip/ThreeColorsFlags.htm

问题:假设有一根绳子,上面有一些红、白、蓝色的旗子。起初旗子的顺序是任意的,现在要求用最少的次数移动这些旗子,使得它们按照蓝、白、红的顺序排列。注意只能在绳子上操作,并且一次只能调换两个旗子。

解法:从绳子的开头(最左边)开始,遇到蓝色的往前移,遇到白色的不动,遇到红色的往后移。

           :我们这里说的前移往左后移往右

        再来看具体的代码实现,先看下图:

        我们可以定义三个指针 a、b、c 。从上图可以看出,a和b指向第一个元素,c指向最后一个元素。a的作用是确保蓝色的旗子移到左边,而c的作用是确保红色的旗子移到右边。b则用于顺序访问各个旗子。

        思路如下:查看b所指向的旗子的颜色,如果是白色的,则不移动任何旗子(因为要求白色的旗子在中间,所以不去动它),将b往后移一位。

                    如果是红色的,此时将b所指向的旗子和c所指向的旗子交换位置,同时c往前移一位。(交换后c指向的已经是红色的旗子了,所以可以往前移一位了)。

                    如果是蓝色的,此时将b所指向的旗子和a所指向的旗子交换位置,同时a往后移一位。(交换后a指向的已经是蓝色的旗子了,所以可以往后移一位了)。这里要注意的是b也要后移一位。(这里我也没有弄得很清楚。大概是b不能在a的后面)

        那么什么时候停止交换呢?显然是c都跑到b的后面去的时候,即b>c

        根据以上的分析,可以写出如下代码:

import java.util.*;
public class ThreeColorFlag {
	
	public static void main(String[] args) {         
		
		  System.out.println("请输入三色旗的顺序(例如 BRRWWB):");
		  Scanner scanner=new Scanner(System.in);
		  String s=scanner.next();
		  ThreeColorFlag tcf=new ThreeColorFlag();
		  s=tcf.move(s.toUpperCase().toCharArray());
		  System.out.println("排列好后的顺序:"+s);
	}
	//互相交换
	public void change(char[] flags,int x,int y){
		
		System.out.println("交换前:"+new String(flags));
		char temp=flags[x];
		flags[x]=flags[y];
		flags[y]=temp;	
		System.out.println((x+1)+" 号和 "+(y+1)+" 号交换");
		System.out.println("交换后:"+new String(flags));
	}
	public String move(char[] flags){
		
		 int a=0,b=0;
		 int c=flags.length-1;
		 
		 while(b<=c){
			 switch (flags[b]){
			   case 'W':
					b++;
					break;
			   case 'B':
				   change(flags, a, b);
				   a++;
				   b++;
				   break;
			   case 'R':
				   
				   change(flags, b, c);
				   c--;
				   break;
			 }	  
			
		 }
		 return new String(flags);
	}
}

运行结果:

请输入三色旗的顺序(例如 BRRWWB):
BBRWBWR
交换前:BBRWBWR
1 号和 1 号交换
交换后:BBRWBWR
交换前:BBRWBWR
2 号和 2 号交换
交换后:BBRWBWR
交换前:BBRWBWR
3 号和 7 号交换
交换后:BBRWBWR
交换前:BBRWBWR
3 号和 6 号交换
交换后:BBWWBRR
交换前:BBWWBRR
3 号和 5 号交换
交换后:BBBWWRR
排列好后的顺序:BBBWWRR

        查看以上的结果,我们发现了一些冗余的操作,比如1 号和 1 号交换、2 号和 2 号交换。这显然是多余的,只需要注意一些细节就可以避免它们。

        第一,当b指向的是红色而和c交换时,如果此时c指向的是红色,显然没有必要把两个红色的旗子交换。所以这时应该把c前移,直到c指向的不是红色的时候才和b指向的旗子交换。当然移动时必须确保b<c。

        第二,同样地,当b指向的是蓝色而要和a的指向交换时,如果a指向的不是蓝色,则交换。否则如果a指向的是蓝色,则也没有必要把两个蓝色的旗子交换。所以这时应该把a和b都往后移一位。

        a和b的交换总有一些特殊。大概是因为它们初始都指向第一个元素的缘故吧。如果是从右往左考察每个旗子,也就是说b和c初始指向最后一个元素,那么就该轮到b和c的交换特殊了吧。

        改进后的代码:

import java.util.*;
public class ThreeColorFlag {
	
	public static void main(String[] args) {         
		
		  System.out.println("请输入三色旗的顺序(例如 BRRWWB):");
		  Scanner scanner=new Scanner(System.in);
		  String s=scanner.next();
		  ThreeColorFlag tcf=new ThreeColorFlag();
		  s=tcf.move(s.toUpperCase().toCharArray());
		  System.out.println("排列好后的顺序:"+s);
	}
	//互相交换
	public void change(char[] flags,int x,int y){
		
		System.out.println("交换前:"+new String(flags));
		char temp=flags[x];
		flags[x]=flags[y];
		flags[y]=temp;	
		System.out.println((x+1)+" 号和 "+(y+1)+" 号交换");
		System.out.println("交换后:"+new String(flags));
	}
	public String move(char[] flags){
		
		 int a=0,b=0;
		 int c=flags.length-1;
		 
		 while(b<=c){
			 switch (flags[b]){
			   case 'W':
					b++;
					break;
			   case 'B':
				   if(flags[a]=='B')
					   {a++;b++;}
				   else{
				   change(flags, a, b);
				   a++;
				   b++;
				   }
				   break;
			   case 'R':
				   while(b<c && flags[c]=='R')
					   c--;
				   change(flags, b, c);
				   c--;
				   break;
			 }	  
			
		 }
		 return new String(flags);
	}
}


运行结果:

请输入三色旗的顺序(例如 BRRWWB):
BBRWBWR
交换前:BBRWBWR
3 号和 6 号交换
交换后:BBWWBRR
交换前:BBWWBRR
3 号和 5 号交换
交换后:BBBWWRR
排列好后的顺序:BBBWWRR

        这就是我们要的最少的交换步骤了。

~完~



 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值