農夫が川を渡る問題C++再帰実現

4320 ワード

ある農夫は狼と羊と白菜を連れて、川の南岸にいた.彼はこれを
いくつかのものはすべて北岸に運ばれた.彼の前にはボートが1隻しかないので,船は彼と荷物を収容するしかない.
品物、それから農夫だけが船を支えることができます.農夫がその場にいたら、狼は羊を食べられず、羊は食べられない.
白菜を食べて、さもなくばオオカミは羊を食べて、羊は白菜を食べて、だから農夫は羊と白菜を残してはいけません
自分が離れても、オオカミと羊を残して自分で離れてはいけない.オオカミは白菜を食べない.農夫将を出すように頼む
すべてのものが川を運ぶ案.
/*
 * Farmer cross river with wolf, sheep and cabbage
 * Depth first search implementation.
 * Author: ProHanziiee
 * E-mail: [email protected]
 * Date: 2017-11-16
 */
#include 

using namespace std;

enum Item{nothing, wolf, sheep, cabbage};

map foodChain;
map reverseFoodChain;
set sideA, sideB;

bool check(set s) {
  for(auto &x : s) {
    if(s.find(foodChain[x]) != s.end() || s.find(reverseFoodChain[x]) != s.end()) return false;
  }
  return true;
}

vector v;
map >, pair > > trace;
pair > endPath;
bool dfs(int dir, Item item, pair > path) { 
  if(dir == 0) { // dir = 0 means transfer item(may be nothing) from sideA to sideB
    if(item != nothing) sideB.insert(item);    
    if((int)sideB.size() == 3)  { // Okay, all items have heen transfered from sideA to sideB
      endPath = path;
      return true;
    }
    if(check(sideB) == true) {
      endPath = make_pair(path.first + 1, make_pair(1, nothing));
      trace[endPath] = path;
      if(dfs(1, nothing, endPath) == true) return true;
    } 
    for(int i = 0; i < 3; ++i) {
      Item tmp = v[i];
      if(tmp == item || sideB.find(tmp) == sideB.end()) continue;
      sideB.erase(sideB.find(tmp));
      if(check(sideB) == true) {
        endPath = make_pair(path.first + 1, make_pair(1, tmp));
        trace[endPath] = path;
        if(dfs(1, tmp, endPath) == true) return true;
      }
      sideB.insert(tmp);
    }
    sideB.erase(item);
  } else { // dir = 1 means transfer item(may be nothing) from sideB to sideA
    if(item != nothing) sideA.insert(item);
    if(sideA.size() == 3) return false;
    for(int i = 0; i < 3; ++i) {
      Item tmp = v[i];
      if(tmp == item || sideA.find(tmp) == sideA.end()) continue;
      sideA.erase(sideA.find(tmp));
      if(check(sideA) == true) {
        endPath = make_pair(path.first + 1, make_pair(0, tmp));
        trace[endPath] = path;
        if(dfs(0, tmp, endPath) == true) return true;
      }
      sideA.insert(tmp);
    }
  }
  return false;
}


void init() {
  trace.clear();
  sideA.clear();
  sideB.clear();
  foodChain.clear();
  reverseFoodChain.clear();
  foodChain[wolf] = sheep;
  reverseFoodChain[sheep] = wolf;
  foodChain[sheep] = cabbage;
  reverseFoodChain[cabbage] = sheep;
  v.clear();
  v.push_back(wolf);
  v.push_back(sheep);
  v.push_back(cabbage);
}

void outputItem(Item item) {
  switch(item) {
    case wolf: puts("Wolf");
      break;
    case sheep: puts("Sheep");
      break;
    case cabbage: puts("Cabbage");
      break;
    default: puts("Nothing");
  }
}

void outputDirection(int dir) {
  if(dir == 0) { printf("A -> B with "); }
  else { printf("B -> A with "); }
}

void outputTrace() {
   puts("A is the initial side of river while B is the target side!");
   while(endPath != make_pair(-1, make_pair(-1, nothing))) {
     outputDirection(endPath.second.first); 
     outputItem(endPath.second.second);
     endPath = trace[endPath];
   }
}

int main() {
  #ifdef Zonda_R
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
  #endif
  init();  
  sideA.insert(wolf);
  sideA.insert(sheep);
  sideA.insert(cabbage);
 
  bool valid = false; 
  
  for(int i = 0; i < 3; ++i) {
    Item tmp = v[i];
    if(sideA.find(tmp) != sideA.end()) {
      sideA.erase(sideA.find(tmp));  
      if(check(sideA) == true) { 
        trace.clear();
        endPath = make_pair(0, make_pair(0, tmp)); 
        trace[endPath] = make_pair(-1, make_pair(-1, nothing));
        if(dfs(0, tmp, endPath) == true) {
          valid = true; 
          break; 
        }
      }
      sideA.insert(tmp);
    }
  }
  if(valid == true) {
    outputTrace();
  } else {
    puts("No Solution!");
  }
  return 0;
}