问题描述
三色旗的问题最早由E.W.Dijkstra所提出,他所使用的用语为DutchNation Flag(Dijkstra为荷兰人),而多数的作者则使用Three-Color Flag来称之。
假设有一条绳子,上面有红、白、蓝三种颜色的旗子,起初绳子上的旗子颜色并没有顺序,您希望将之分类,并排列为蓝、白、红的顺序,要如何移动次数才会最少,注意您只能在绳子上进行这个动作,而且一次只能调换两个旗子。
解法
三种颜色的旗子,白色居中,蓝色开头,红色结尾,如果想要移动的次数最少,那么需要做的就是红色的往后面移动,蓝色的往前面移动,中间的白色尽量不动。
算法的流程就是用三个信标b,w,r分别指向不同的旗子。其中b指向的从0开始连续排列的Blue旗子的最后面的第一个非Blue旗子,r指向的从最后一个序号开始连续排列的Red旗子的第一非Red旗子。例如:bbrwbbrr,那么b指向的就是序号为2的r,r指向的就是序号为倒数第三的b。w在这里里面起到的作用就是一个可以移动的指针。
当w指向的旗子是White旗子的时候,w继续向前移动;当w指向的旗子是Blue的时候,就需要把b所指的旗子和w所指的Blue旗子交互;同理当w指的旗子是Red的时候就需要w所指的Red旗子和r所指的旗子交换。
下图是起始时三个信标所指向的位置。
补充思考:
只能在一条绳子上移动,表示不能使用其它的辅助数组。问题的要实现三种旗子的分类,这里我们引入三个变量 bFlag, wFlag, rFlag 做标志,如图所示:
变量意义:
区间 | 意义 |
---|---|
[0,bflag) | 已经分好的蓝色旗子 |
[bflag,wflag) | 已经分好的白色旗子 |
[wflag,rflag) | 未分类的旗子 |
[rflag,len) | 已经分好的红色旗子 |
其中len表示绳子上旗子总个数, 这里的区间表示的是数组下标或旗子编号(0~len-1)区间。
用wFlag位置做判断标准,每次进行如下处理:
- 遇到蓝色旗子,交换wFlag和bFlag位置的旗子,并且wFlag和bFlag都向后移动一个位置。
- 遇到白色旗子,不处理,wFlag向后移动一个位置。
- 遇到红色旗子,交换wFlag和rFlag位置的旗子,rFlag向前移动若干个位置直到为非红色旗子,注意不移动wFlag,因为交换后wFlag位置不一定是白色旗子。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
char color[100];
cin>>color;
int wflag=0,bflag=0,rflag=strlen(color)-1;
int minstep=0;
while(wflag<=rflag)
{
if(color[wflag]=='b')
{
if(bflag!=wflag)
{
swap(color[bflag],color[wflag]);
minstep++;
};
bflag++;
wflag++;
}
else if(color[wflag]=='w')
wflag++;
else
{
while(wflag<rflag&&color[rflag]=='r')
rflag--;
if(wflag!=rflag)
{
swap(color[wflag],color[rflag]);
minstep++;
}
rflag--;
}
}
cout<<color<<endl;
cout<<"minstep="<<minstep<<endl;
return 0;
}