问题:假设有一根绳子,上面有一些红、白、蓝色的旗子。起初旗子的顺序是任意的,现在要求用最少的次数移动这些旗子,使得它们按照蓝、白、红的顺序排列。注意只能在绳子上操作,并且一次只能调换两个旗子。
盗一张图:
找了些例子,做法是用三个指针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));
}
}
有的地方也还不太确定,发来记录一下。