Jul 19, 2011
Jul 19, 2011
N/A Views
MD
warning
この記事は2年以上前に更新されたものです。情報が古くなっている可能性があります。

探索問題復習シリーズその2。宣教師と人食い人種問題を幅優先探索で解いてみた。

一応ルールを張っとく

  • 3人の宣教師と3人の人食い人種が本土から対岸に2人乗りのボートで渡ろうとしている。
  • ボートの上や川岸で宣教師の数より人食い人種の数が多くなると,宣教師は人食い人種に食べられる。
  • 宣教師が食べられることなく川を渡りきるにはどうしたら良いか?

以下、適当Java実装。

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

public class Cannibalism {
    public static class State {
        // 本土={宣教師の数、人食い人種の数}
        int hondo[] = new int[2];
        // 対岸={宣教師の数、人食い人種の数}
        int taigan[] = new int[2];
        // ボート(true = 本土側、false=対岸側)
        boolean boat = true;
        // 前状態
        State prev;
        // 手数
        int depth = 0;

        List<State> movableStatees() {
            List<State> states = new ArrayList<State>();
            // 移動する可能性のある組み合わせ(宣教師、人食い人種)
            int[][] vv = { { 1, 0 }, { 2, 0 }, { 1, 1 }, { 0, 1 }, { 0, 2 } };
            for (int[] v : vv) {
                int[] h = hondo.clone();
                int[] t = taigan.clone();
                if (boat) {
                    // 本土側
                    h[0] -= v[0];
                    h[1] -= v[1];
                    t[0] += v[0];
                    t[1] += v[1];
                } else {
                    // 対岸側
                    h[0] += v[0];
                    h[1] += v[1];
                    t[0] -= v[0];
                    t[1] -= v[1];
                }
                if (validState(h, t)) {
                    State s = new State();
                    s.hondo = h;
                    s.taigan = t;
                    s.boat = !boat;
                    s.prev = this;
                    s.depth = depth + 1;
                    states.add(s);
                }
            }
            return states;
        }

        static boolean validState(int[] hondo, int[] taigan) {
            // 負の数にはならない
            if (hondo[0] < 0 || taigan[0] < 0 || hondo[1] < 0 || taigan[1] < 0) {
                return false;
            }
            // 本土の宣教師数が人食い人種数より少なくなると不正
            if (hondo[0] > 0 && hondo[0] < hondo[1]) {
                return false;
            }
            // 対岸の宣教師数が人食い人種数より少なくなると不正
            if (taigan[0] > 0 && taigan[0] < taigan[1]) {
                return false;
            }
            return true;
        }

        public void printTrace() {
            Deque<State> d = new ArrayDeque<State>();
            State s = this;
            while (s != null) {
                d.addFirst(s);
                s = s.prev;
            }
            System.out.println(d);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(System.getProperty("line.separator"));
            for (int i = 0; i < 3; i++) {
                if (hondo[0] > i) {
                    sb.append("M");
                } else {
                    sb.append(" ");
                }
                if (hondo[1] > i) {
                    sb.append("C");
                } else {
                    sb.append(" ");
                }
                sb.append("|");
                if (i == 1) {
                    if (boat) {
                        sb.append(">  ");
                    } else {
                        sb.append("  <");
                    }
                } else {
                    sb.append("   ");
                }
                sb.append("|");
                if (taigan[0] > i) {
                    sb.append("M");
                } else {
                    sb.append(" ");
                }
                if (taigan[1] > i) {
                    sb.append("C");
                } else {
                    sb.append(" ");
                }
                sb.append(System.getProperty("line.separator"));
            }
            return sb.toString();
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + (boat ? 1231 : 1237);
            result = prime * result + Arrays.hashCode(hondo);
            result = prime * result + Arrays.hashCode(taigan);
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            State other = (State) obj;
            if (boat != other.boat)
                return false;
            if (!Arrays.equals(hondo, other.hondo))
                return false;
            if (!Arrays.equals(taigan, other.taigan))
                return false;
            return true;
        }
    }

    public static State breadthFirstSearch(State initial, State goal) {
        Set<State> closed = new HashSet<State>();
        Queue<State> open = new LinkedList<State>();
        initial.depth = 0;
        open.add(initial);

        while (!open.isEmpty()) {
            State s = open.poll();
            closed.add(s);
            for (State next : s.movableStatees()) {
                if (closed.contains(next)) {
                    continue;
                }
                if (goal.equals(next)) {
                    System.out.println("goal");
                    System.out.println("depth=" + next.depth);
                    System.out.println("closed=" + closed.size());
                    return next;
                }
                open.add(next);
            }

        }

        return null;
    }

    public static void main(String[] args) {
        State initial = new State();
        initial.hondo[0] = 3;
        initial.hondo[1] = 3;
        initial.taigan[0] = 0;
        initial.taigan[1] = 0;
        initial.boat = true;

        State goal = new State();
        goal.hondo[0] = 0;
        goal.hondo[1] = 0;
        goal.taigan[0] = 3;
        goal.taigan[1] = 3;
        goal.boat = false;

        State result = breadthFirstSearch(initial, goal);

        if (result != null) {
            result.printTrace();
        } else {
            System.out.println("NG");
        }
    }
}

実行結果

goal
depth=11
closed=13
[
MC|   |  
MC|>  |  
MC|   |  
, 
MC|   |MC
MC|  <|  
  |   |  
, 
MC|   | C
MC|>  |  
M |   |  
, 
M |   | C
M |  <| C
M |   | C
, 
MC|   | C
M |>  | C
M |   |  
, 
MC|   |MC
  |  <|MC
  |   |  
, 
MC|   |MC
MC|>  |  
  |   |  
, 
 C|   |MC
 C|  <|M 
  |   |M 
, 
 C|   |M 
 C|>  |M 
 C|   |M 
, 
 C|   |MC
  |  <|MC
  |   |M 
, 
MC|   |MC
  |>  |MC
  |   |  
, 
  |   |MC
  |  <|MC
  |   |MC
]
Found a mistake? Update the entry.
Share this article: