三色旗问题

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

盗一张图:

找了些例子,做法是用三个指针abc,a指向开头,c指向末尾。b从开头至末尾移动,如果发现是蓝色,和a指向的非蓝色交换;如果是白色,则不做任何操作;如果是红色,和c指向的非红色交换.

代码如下:

public static void main(String[] args) {

 System.out.println("请输入三色旗的顺序");
 Scanner scanner=new Scanner(System.in);
 String s=scanner.next();
 s=move(s.toUpperCase().toCharArray());
 System.out.println("排列好后的顺序:"+s);
 }
// 互相交换
 public static 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 static 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);
 }
但是好像这种做法会存在一些无效的移动,例如BWBWWBR的序列,实际上只需要移动一次,但却移动了2次.

然后换了一种做法,麻烦一点,但似乎可以做到避免无效的移动。

代码如下:

import java.util.Scanner;

public class ThreeColorFlags {
	static int moveTime = 0;

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入一个序列:(如:BBBWWRR)");
		String input = scanner.nextLine();
		input = input.toUpperCase();
		char[] flags = input.toCharArray();
		// 先统计蓝色和白色共有多少
		int countB = 0, countW = 0;
		int length = flags.length;
		for (char c : flags) {
			switch (c) {
			case 'B':
				countB++;
				break;
			case 'W':
				countW++;
				break;
			}
		}
		moveAvoidInvalid(flags, countB, countW, length);
	}

	public static void moveAvoidInvalid(char[] flags, int countB, int countW, int length) {
		// 第一步先将蓝区移动完成
		// 先遍历蓝区(B区),找到W,优先去和W区的B交换;找到R,优先去和R区的B交换
		// 用三个指针分别指向三个区域
		int pointB = 0;
		int pointW = countB;// 从W区正向开始找
		int pointR = length - 1;// R区逆向开始找
		while (pointB < countB) {
			switch (flags[pointB]) {
			case 'B':
				pointB++;
				break;
			case 'W':
				if (flags[pointW] == 'B') {
					exchange(flags, pointB, pointW);
					pointB++;
					pointW++;
				} else {
					pointW++;
				}
				break;
			case 'R':
				if (flags[pointR] == 'B') {
					exchange(flags, pointB, pointR);
					pointB++;
					pointR--;
				} else {
					pointR--;
				}
				break;
			}
		}

		// 经过上一步,蓝色应该移动好了
		// 第二步,完成白色和红色的移动
		// 让指针重新指向W去开始和R区的末尾
		pointW = countB;
		pointR = length - 1;
		while (pointW < countB + countW) {
			switch (flags[pointW]) {
			case 'W':
				pointW++;
				break;
			case 'R':
				if (flags[pointR] == 'W') {
					exchange(flags, pointW, pointR);
					pointW++;
					pointR--;
				} else {
					pointR--;
				}
				break;
			}
		}
	}

	public static void exchange(char[] flags, int x, int y) {
		moveTime++;
		System.out.println("第" + moveTime + "次交换前的序列:" + String.valueOf(flags));
		char temp = flags[x];
		flags[x] = flags[y];
		flags[y] = temp;
		System.out.println("第" + moveTime + "次交换后的序列:" + String.valueOf(flags));
	}
}

有的地方也还不太确定,发来记录一下。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值