三色旗问题

本文链接:http://www.cnblogs.com/xxNote/articles/3965687.html

 

问题:假设有一根绳子,上面有一些红、白、蓝色的旗子。起初旗子的顺序是任意的,现在要求用最少的次数移动这些旗子,使得它们按照蓝、白、红的顺序排列。注意只能在绳子上操作,并且一次只能调换两个旗子。
解法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 ,然后按从小到大进行排序。

 

posted @ 2014-09-11 09:02  xxNote  阅读(994)  评论(0编辑  收藏  举报