1. 程式人生 > 其它 >演算法-經典趣題-三色旗

演算法-經典趣題-三色旗

一、問題

三色旗的問題最早由E.W.Dijkstra所提出,大致意思如下:

有一條繩子上面掛有白、紅、藍三種顏色的多面旗子,這些旗子的排列是無序的。現在要將繩子上的旗子按藍、白、紅三種顏色進行歸類排列,但是隻能在繩子上進行旗子的移動,並且每次只能調換兩個旗子。問如何採用最少的步驟來完成三色旗的排列呢?

二、分析

我們來分析一下三色旗問題。假設繩子上共有10面旗子,藍色旗子用符號b表示,白色旗子用符號w表示,紅色旗子用符號r表示,然後排成一列

定義3個變數(Blue、Write、Red)來指示三種顏色的旗

在0~(Blue-1)之間放藍色旗;

Blue~(Write-1)放白色旗;

剩餘的位置放紅色旗。

三個變數的初始位置

每一次都處理變數Write指向位置的元素,可分如下3種情況處理:

  • 如果Write所在位置的元素是紅旗r,表示需將紅旗與Red變數的元素對調,然後將Red--,繼續處理下一個位置
  • 如果White所在位置的元素是白旗w,表示該位置的元素應該在此,然後將White++,繼續處理下一個位置
  • 如果White所在位置的元素是藍旗b,表示需將藍旗與Blue變數所在位置的元素對調,然後將Blue++、White++

最終結果:

三、程式設計

package com.joshua317;

import java.util.Arrays;

public class Main {
    static int count;
    static char color[] = "brwwrbrbwr".toCharArray();
    static int Blue, White, Red;
    public static void main(String[] args) {
        int i;
        Blue = 0;
        White = 0;
        Red = color.length - 1;
        count = 0;

        System.out.println("三色旗問題求解");
        System.out.println("三色旗最初排列:");
        for (i = 0; i < color.length; i++) {
            System.out.printf(" %c", color[i]);
        }
        System.out.println();

        threeFlags();

        System.out.printf("通過%d次完成對調後,結果如下:", count);
        for (i = 0; i < color.length; i++) {
            System.out.printf(" %c", color[i]);
        }
    }

    /**
     * 調換順序
     * @param c
     * @param x
     * @param y
     */
    static void swap(char[] c, int x, int y)
    {
        int i;
        char temp;
        temp = c[x];
        c[x] = c[y];
        c[y] = temp;
        count++;

        System.out.printf("第%d次對調後:", count);
        for (i = 0; i < color.length; i++) {
            System.out.printf(" %c", color[i]);
        }
        System.out.println();
    }

    /**
     * 三色旗演算法
     */
    static void threeFlags()
    {
        //如果開頭已經是藍旗,直接將Blue++、White++
        while (color[White] == 'b') {
            Blue++;
            White++;
        }

        //如果結尾已經是紅旗,直接將Red--
        while (color[Red] == 'r') {
            Red--;
        }

        //剩下未處理的元素繼續處理
        while (White <= Red) {
            //如果White所在位置的元素是紅旗r,表示需將紅旗與Red變數的元素對調,然後將Red--,,繼續處理下一個位置
            if (color[White] == 'r') {
                swap(color, White, Red);
                Red--;
                //如果Red所在位置的元素是紅旗r,繼續向前移動Red位置,即Red--
                while (color[Red] == 'r') {
                    Red--;
                }
            }

            //如果White所在位置的元素是白旗w,表示該位置的元素應該在此,然後將White++,繼續處理下一個位置
            while (color[White] == 'w') {
                White++;
            }

            //如果White所在位置的元素是藍旗b,表示需將藍旗與Blue變數所在位置的元素對調,然後將Blue++、White++
            if (color[White] == 'b') {
                swap(color, White, Blue);
                Blue++;
                White++;
            }
        }
    }
}