三色旗问题

最后更新于:2022-04-01 10:11:39

假设有一根绳子,上面有一些红、白、蓝色的旗子。起初旗子的顺序是任意的,现在要求用最少的次数移动这些旗子,使得它们按照蓝、白、红的顺序排列。注意只能在绳子上操作,并且一次只能调换两个旗子。 分析:其实要移动旗子得到要求的结果很简单,但是需要注意的是需要移动最少的次数这个限制条件。 网上的一种解法: 从绳子开头进行,遇到蓝色往前移,遇到白色留在中间,遇到红色往后移,如下所示: 只是要让移动次数最少的话,就要有些技巧: 如果图中W所在的位置为白色,则W+1,表示未处理的部份移至至白色群组。 如果W部份为蓝色,则B与W的元素对调,而B与W必须各+1,表示两个群组都多了一个元素。 如果W所在的位置是红色,则将W与R交换,但R要减1,表示未处理的部份减1。 注意B、W、R并不是三色旗的个数,它们只是一个移动的指标;什幺时候移动结束呢?一开始时未处理的R指标会是等于旗子的总数,当R的索引数减至少于W的索引数时,表示接下来的旗子就都是红色了,此时就可以结束移动,如下所示: 该程序选自:经典算法大全 老奔整理 Email: [ben0133@163.com](#) ~~~ #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BLUE 'b' #define WHITE 'w' #define RED 'r' using namespace std; int times = 0; char color[] = {'r', 'w', 'b', 'w', 'w', 'b', 'r', 'b', 'w', 'r', '\0'}; //#define SWAP(x, y) { char temp; \ // temp = color[x]; \ // color[x] = color[y]; \ // color[y] = temp; } void printArr() { cout<<"第"<<times<<"次的排序结果为:"; for (int i =0; i<strlen(color);i++) { cout<<color[i]; } cout<<endl; } void SWAP(int x, int y) { char temp; temp = color[x]; color[x] = color[y]; color[y] = temp; times++; printArr(); }; int main() { int wFlag = 0; int bFlag = 0; int rFlag = strlen(color) - 1; int i; cout<<"移动前:"; 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--; } } cout<<"移动后:"<<endl; for(i = 0; i < strlen(color); i++) printf("%c", color[i]); printf("\n"); cout<<"cost time is :"<<times<<endl; system("pause"); return 0; } ~~~ 程序执行结果为: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5c49a92406.jpg) 注意观察打印出来的结果,可以发现有一些冗余的移动,导致移动次数比较大改进的解法如下:进行两趟遍历,首先对于成对的顺序相反的红色和蓝色旗子,交互其位置其次对前后的蓝色和红色旗帜进行整理,使其聚集到一起 ~~~ //三色旗问题改进 //可大体分为三步,首先交换b和r的位置,直到所有b都在r之前; //然后整理前半部分的b,使其都靠前 //最后整理后半部分的r,使其都靠后 #include <iostream> using namespace std; int g_nExchangeNum = 0; char szFlagInput[1024] = {'r','w','b','w', 'w', 'b', 'r', 'b', 'w', 'r', '\0'}; //char szFlagInput[1024] = {"rwwwwrbbbrrwrrbbrrrbbbbwwwbrr"}; void ExchangeColor(char *pSour, char *pDes) { char szTemp; g_nExchangeNum++; szTemp = *pSour; *pSour = *pDes; *pDes = szTemp; cout<<"第"<<g_nExchangeNum<<"次移动,结果为: "<<szFlagInput<<endl; } //r和b互换 void ScanFrist() { char *pBlue = szFlagInput; char *pRed = szFlagInput + strlen(szFlagInput) - 1; while (pRed > pBlue) { while (pBlue < szFlagInput + strlen(szFlagInput) - 1 && *pBlue != 'r' ) { pBlue++; } while(pRed > szFlagInput && *pRed!= 'b') pRed--; if(pRed > pBlue) ExchangeColor(pBlue, pRed); } } //蓝色旗帜调整完毕后,调整红色旗帜 void ScanSecond() { char *pWhite = NULL; char *pBlue = NULL; char *pRed = NULL; //调整蓝色旗帜 pRed = szFlagInput ; while (pRed < szFlagInput+ strlen(szFlagInput)) { if (*pRed != 'r') { pRed++; } else break; } if (pRed != szFlagInput) //有蓝色的旗帜 { if(pRed == szFlagInput + strlen(szFlagInput) ) pBlue = szFlagInput + strlen(szFlagInput) -1; else pBlue = pRed - 1; pWhite = szFlagInput; while(pWhite < pBlue) { while(pWhite< szFlagInput + strlen(szFlagInput) - 1 && *pWhite != 'w') pWhite++; while(pBlue > szFlagInput && *pBlue!= 'b') pBlue--; if (pWhite < pBlue) { ExchangeColor(pWhite,pBlue); } } } //调整红色旗帜 if(pRed == szFlagInput + strlen(szFlagInput)) return; pWhite = szFlagInput + strlen(szFlagInput) -1; while (pWhite > pRed) { while(pRed< szFlagInput + strlen(szFlagInput) - 1 && *pRed != 'r') pRed++; while( pWhite> szFlagInput && *pWhite!= 'w') pWhite--; if (pWhite > pRed) { ExchangeColor(pWhite,pRed); } } } int main() { cout<<"未移动前结果为: "<<szFlagInput<<endl; ScanFrist(); ScanSecond(); cout<<"the color list of the result is :"<<endl; cout<<" "<<szFlagInput<<endl; cout<<"总共的交换次数为:"<<g_nExchangeNum<<endl; system("pause"); return 1; } ~~~ 程序执行结果为3,无冗余移动步骤: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5c49aad147.jpg)
';