算法-经典趣题-三色旗

一、问题

三色旗的问题最早由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++;
            }
        }
    }
}

 

算法-经典趣题-三色旗  

 

 

上一篇:一个好玩的现象:getline() cannot be used after Cin directly


下一篇:CSS 解决文章标题过长换行显示问题 white-space:pre-line;