前言
This Series aritcles are all based on the book 《经典算法大全》; 对于该书的所有案例进行一个探究和拓展,并且用python和C++进行实现; 目的是熟悉常用算法过程中的技巧和逻辑拓展。
提出问题
Algorithm Gossip: 三色棋(Three_Color_Flag)
假设数组里面有若干个3种数 (1,2,3)要大的数靠后,小的数靠前,每次只能调换两个数,问怎么移动让次数最少。
原题目:绳子上有三种三色的旗, “蓝白红”按照这个顺序分离,每次换两个旗。方案让最小换的次数。
分析和解释
在一条绳子上移动,在程式中也就意味只能使用一个阵列,而不使用其它的阵列来作辅助,问
题的解法很简单,您可以自己想像一下在移动旗子,从绳子开头进行,遇到蓝色往前移,遇到
白色留在中间,遇到红色往后移,如下所示:
只是要让移动次数最少的话,就要有些技巧:
- 如果图中W所在的位置为白色,则W+1,表示未处理的部份移至至白色群组。
- 如果W部份为蓝色,则B与W的元素对调,而B与W必须各+1,表示两个群组都多了一个元素 。
- 如果W所在的位置是红色,则将W与R交换,但R要减1,表示未处理的部份减1。
注意B、W、R并不是三色旗的个数,它们只是一个移动的指标;什幺时候移动结束呢?一开始
时未处理的R指标会是等于旗子的总数,当R的索引数减至少于W的索引数时,表示接下来的旗
子就都是红色了,此时就可以结束移动
代码
c/c++ 实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLUE 'b'
#define WHITE 'w'
#define RED 'r'
#define SWAP(x,y) { char temp; \
temp = color[x]; \
color[x] = color[y]; \
color[y] = temp; }
int main()
{
char color[] = {'r', 'w', 'b', 'w', 'w','b', 'r', 'b', 'w', 'r', '\0'};
int wFlag = 0;
int bFlag = 0;
int rFlag = strlen(color) - 1;
int i;
for(i = 0; i < strlen(color); i++)
printf("%c ", color[i]);
printf("\n");
while(wFlag <= rFlag) {
if(color[wFlag] == WHITE)
wFlag++;
else if(color[wFlag] == BLUE)
{
SWAP(bFlag,wFlag);
bFlag++; wFlag++;
}else
{
while(wFlag < rFlag && color[rFlag] == RED)
rFlag--;
SWAP(rFlag,wFlag);
rFlag--;
}
}
for(i = 0; i < strlen(color); i++)
printf("%c ", color[i]);
printf("\n");
return 0;
}
python 实现
def printc(color):
for i in range(len(color)):
print(color[i],end = "")
print()
color = ['r', 'w', 'b', 'w', 'w','b', 'r', 'b', 'w', 'r']
wFlag = 0;
bFlag = 0;
rFlag = len(color) - 1
printc(color)
while(wFlag <= rFlag):
if(color[wFlag] == 'w'):
wFlag+=1;
elif(color[wFlag] == 'b'):
color[bFlag],color[wFlag] = color[wFlag],color[bFlag]
bFlag+=1; wFlag+=1;
else:
while(wFlag < rFlag and color[rFlag] == 'r'):
rFlag-=1
color[rFlag],color[wFlag] = color[wFlag],color[rFlag]
rFlag-=1;
printc(color)
拓展和关联
有兴趣的可以尝试下,是不是最优解,用动态规划验证下。
后记
可以向dp状态转变。/后面再深究。
参考书籍
- 《经典算法大全》
- 维基百科