三色旗问题
本文链接:http://www.cnblogs.com/xxNote/articles/3965687.html
问题:假设有一根绳子,上面有一些红、白、蓝色的旗子。起初旗子的顺序是任意的,现在要求用最少的次数移动这些旗子,使得它们按照蓝、白、红的顺序排列。注意只能在绳子上操作,并且一次只能调换两个旗子。
解法1:
这种方法比较容易理解,同时也属于一种低效的方法。用 for 循环进行三次遍历,可以定义两个变量,变量 todo 用来指向要进行替换的位置,变量 s 指向正在遍历的位置。
解法1:
这种方法比较容易理解,同时也属于一种低效的方法。用 for 循环进行三次遍历,可以定义两个变量,变量 todo 用来指向要进行替换的位置,变量 s 指向正在遍历的位置。
先遍历第一次,当遇到B的时候(即s指向B时)把s指向的旗子与todo指向的那个替换,todo加一,遍历直到s指向最后一个位置之后停止。
接着开始遍历第二次,这次寻找W,s从todo开始,遇到W的时候,把s指向的旗子与todo指向的那个替换,todo加一,遍历直到s指向最后一个位置之后停止。
接着开始遍历第三次,这次寻找R,s从todo开始,遇到R的时候,把s指向的旗子与todo指向的那个替换,todo加一,遍历直到s指向最后一个位置之后停止。
这种方法最好的情况是全是B的时候,需要遍历三色旗的个数 n 次
最差的情况是全是R的时候,需要遍历 3n 次
完整代码:
1 /* 2 ******************************************************************************* 3 时间 :2014年9月9日 08:02:13 4 程序名:三色旗解法1.c 5 用途 :对三色旗进行排序(蓝色B,白色W,红色R,排序后蓝色在前白色在中间红色在最后) 6 By :xxNote 7 算法分析:这种方法比较容易理解,同时也属于一种低效的方法。可以定义两个变量,用 8 for 循环进行遍历,变量 todo 用来指向要进行替换的位置,变量 s 指向正在遍历的 9 位置,先遍历遍历一边当遇到B的时候(即s指向B时)把s指向的B与todo指向的那个替换,todo加一 10 遍历第一次后,开始遍历第二次,这次寻找W,s从todo开始,接下来同上,遍历第二次后, 11 开始遍历第三次,这次寻找R,s从todo开始,接下来同上 12 这种方法最好的情况是全是B的时候,需要遍历三色旗的个数 n 次 13 最差的情况是全是R的时候,需要遍历 3n 次 14 ******************************************************************************* 15 */ 16 #include <stdio.h> 17 18 int main(void) 19 { 20 int s, todo, len, N;21 char str[1024], tmp; 22 //BBWRWRBWRBWRBWRBWRBWRBWRBWRWBRWRBWRBWRBRRRBWRBWRWWWWWWWWWWWWRBBBRRRRBRRRWW 23 //BRRRWW 24 start: 25 printf("请输入红蓝白字符串代号(RBW):"); 26 scanf("%s", str); 27 printf("你输入的是:%s\n", str); 28 len = strlen(str); 29 N = 0; 30 for (s=0, todo=0; s<len; s++) 31 { 32 if (str[s] == 'B') 33 { 34 tmp = str[todo]; 35 str[todo] = str[s]; 36 str[s] = tmp; 37 todo++; 38 } 39 N++; 40 } 41 for (s=todo; s<len; s++) 42 { 43 if (str[s] == 'W') 44 { 45 tmp = str[todo]; 46 str[todo] = str[s]; 47 str[s] = tmp; 48 todo++; 49 } 50 N++; 51 } 52 for (s=todo; s<len; s++) 53 { 54 if (str[s] == 'R') 55 { 56 tmp = str[todo]; 57 str[todo] = str[s]; 58 str[s] = tmp; 59 todo++; 60 } 61 N++; 62 } 63 64 printf("排序后的字符串是:%s\n", str); 65 printf("字符串长度:%d\n", len); 66 printf("排序的次数是:%d\n", N); 67 68 goto start; 69 return 0; 70 }
当移动到b>c时排序结束。
完整代码:
1 /* 2 ******************************************************************************* 3 时间 :2014年9月9日 08:28:53 4 程序名:三色旗解法2.c 5 用途 :对三色旗进行排序(蓝色B,白色W,红色R,排序后蓝色在前白色在中间红色在最后) 6 By :xxNote 7 算法分析: 8 ******************************************************************************* 9 */ 10 #include <stdio.h> 11 12 int main(void) 13 { 14 int a, b, c, N, H; 15 char str[1024], tmp; 16 //BBWRWRBWRBWRBWRBWRBWRBWRBWRWBRWRBWRBWRBRRRBWRBWRWWWWWWWWWWWWRBBBRRRRBRRRWWBRRRWW 17 start: 18 printf("请输入红蓝白字符串代号(RBW):"); 19 scanf("%s", str); 20 printf("你输入的是:%s\n", str); 21 a = b = 0; 22 c = strlen(str)-1; 23 N = H = 0; 24 25 //开始处理 26 while (b <= c) 27 { 28 switch(str[b]) 29 { 30 case 'B': 31 tmp = str[a]; 32 str[a] = str[b]; 33 str[b] = tmp; 34 a++; 35 b++; 36 H++; 37 break; 38 case 'W': 39 b++; 40 break; 41 case 'R': 42 tmp = str[c]; 43 str[c] = str[b]; 44 str[b] = tmp; 45 c--; 46 H++; 47 break; 48 } 49 N++; 50 } 51 //处理结束 52 53 printf("排序后的字符串是:%s\n", str); 54 printf("字符串长度:%d\n", strlen(str)); 55 printf("循环的次数是:%d, 替换的次数是:%d\n", N, H); 56 57 goto start; 58 return 0; 59 }
运行效果:
输入:BBBBB 可以发现当没有必要进行替换的时候也进行了替换 ,如下
因此,可以加上条件判断来减少替换不必要的替换,改进后程序
1 /* 2 ******************************************************************************* 3 时间 :2014年9月9日 08:28:53 4 程序名:三色旗解法2.c 5 用途 :对三色旗进行排序(蓝色B,白色W,红色R,排序后蓝色在前白色在中间红色在最后) 6 By :xxNote 7 算法分析: 8 ******************************************************************************* 9 */ 10 #include <stdio.h> 11 12 int main(void) 13 { 14 int a, b, c, N, H;//N循环次数,H替换次数 15 char str[1024], tmp; 16 //BBWRWRBWRBWRBWRBWRBWRBWRBWRWBRWRBWRBWRBRRRBWRBWRWWWWWWWWWWWWRBBBRRRRBRRRWWBRRRWW 17 start: 18 printf("请输入红蓝白字符串代号(RBW):"); 19 scanf("%s", str); 20 printf("你输入的是:%s\n", str); 21 a = b = 0; 22 c = strlen(str)-1; 23 N = H = 0; 24 25 //开始处理 26 while (b <= c) 27 { 28 switch(str[b]) 29 { 30 case 'B': 31 if (str[a] != 'B') 32 { 33 tmp = str[a]; 34 str[a] = str[b]; 35 str[b] = tmp; 36 H++; 37 } 38 a++; 39 b++; 40 break; 41 case 'W': 42 b++; 43 break; 44 case 'R': 45 if (str[c] != 'R') 46 { 47 tmp = str[c]; 48 str[c] = str[b]; 49 str[b] = tmp; 50 H++; 51 } 52 c--; 53 break; 54 } 55 N++; 56 } 57 //处理结束 58 59 printf("排序后的字符串是:%s\n", str); 60 printf("字符串长度:%d\n", strlen(str)); 61 printf("循环的次数是:%d, 替换的次数是:%d\n", N, H); 62 63 goto start; 64 return 0; 65 }
运行结果:
输入:BBBBB运行一下
可以发现解法二改进后的循环次数总是等于 字符串的长度即旗子的个数,替换的次数最少替换0次,最多替换旗子的个数减一次。
细想一下,这个问题还可以转化成按从小到大排序问题,B代表1,W代表2,R代表3 ,然后按从小到大进行排序。
细想一下,这个问题还可以转化成按从小到大排序问题,B代表1,W代表2,R代表3 ,然后按从小到大进行排序。