题目:
有N个传教士和N个野人要过河,现在有一条船只能承载M个人,在任何时刻,如果有野人和传教士在一起,必须要求传教士的人数多于或等于野人的人数(包括船上和两个岸边)。
设M为传教士的人数,C为野人的人数,当N=3, M=2时, 用状态空间法求解此问题的过程如下:
(1)设置状态变量并确定值域
S、C = N,B = { 0 , 1} (1表示在岸边),要求在船上S>=C且S+C <= M
初始状态 目标状态
L | R | |
S | 3 | 0 |
C | 3 | 0 |
B | 1 | 0 |
L | R | |
S | 0 | 3 |
C | 0 | 3 |
B | 0 | 1 |
(2)确定状态组,分别列出初始状态集和目标状态集
用三元组来表示(S , C , B)这里面都表示的是左岸状态
其中0<=S , C <= 3 , B∈{ 0 , 1}
(3 , 3 , 1) (0 , 0 , 0)
初始状态表示全部成员在河的的左岸, 目标状态表示全部成员从河的左岸全部渡河完毕。
(3)定义并确定规则集合
仍然以河的左岸为基点来考虑,把船从左岸划向右岸定义为Pij操作。其中,第一个下标i表示船载的传教士人数,第二个下标j表示船载的野人人数。同理,从右岸将船划回左岸称之为Qij操作,下标的定义同前。 则共有10种操作,操作集为:
F={P10, P01,P11,P20,P02,Q10,Q01,Q11,Q20,Q02}
P10 if ( S , C , B=1 ) then ( S–1 , C , B –1 )
P01 if ( S , C , B=1 ) then ( S, C–1 , B –1 )
P11 if ( S , C , B=1 ) then ( S –1 , C–1 , B –1 )
P20 if ( S , C , B=1 ) then ( S –2 , C , B –1 )
P02 if ( S , C , B=1 ) then ( S, C–2 , B –1 )
Q10 if ( S , C , B=0 ) then ( S +1 , C , B+1 )
Q01 if ( S , C , B=0 ) then ( S, C+1 , B +1 )
Q11 if ( S , C , B=0 ) then ( S +1 , C +1, B +1 )
Q20 if ( S , C , B=0 ) then ( S +2 , C +2, B +1 )
Q02 if ( S , C, B=0 ) then ( S, C +2, B +1 )
(4)当状态数量不是很大时,画出合理的状态空间图(略)
用广度优先的方法,求最少次数:
#include <cstdio> #include <iostream> #include <vector> #include <queue> #include <set> using namespace std; // N is the number of scholars/cannibals, M is the boat's capacity int N,M; class BoatState { public: int scholars; int cannibals; }; class LeftRiverSideState { public: int scholars; int cannibals; bool isBoatOnStandby; int countTimes; }; void createBoatState(vector<BoatState> &boatStates) { //i cannibals, j scholars. for(int i=0; i<=M; ++i) { for(int j=0; j<=M; ++j) { if(i+j>0 && i+j<=M && (i<=j||j==0)) { BoatState bState; bState.cannibals = i; bState.scholars = j; boatStates.push_back(bState); } } } } void changeState(LeftRiverSideState &lrsState, queue<LeftRiverSideState> &stateQueue, vector<BoatState> &boatStates) { int countTimes = lrsState.countTimes + 1; if(lrsState.isBoatOnStandby) { for(vector<BoatState>::iterator iter=boatStates.begin(); iter!=boatStates.end(); ++iter) { if(lrsState.cannibals >= iter->cannibals && lrsState.scholars >= iter->scholars) { LeftRiverSideState tmpState; tmpState.cannibals = lrsState.cannibals - iter->cannibals; tmpState.scholars = lrsState.scholars - iter->scholars; tmpState.isBoatOnStandby = false; tmpState.countTimes = countTimes; stateQueue.push(tmpState); } } } else { for(vector<BoatState>::iterator iter=boatStates.begin(); iter!=boatStates.end(); ++iter) { if((N-lrsState.cannibals) >= iter->cannibals && (N-lrsState.scholars) >= iter->scholars) { LeftRiverSideState tmpState; tmpState.cannibals = lrsState.cannibals + iter->cannibals; tmpState.scholars = lrsState.scholars + iter->scholars; tmpState.isBoatOnStandby = true; tmpState.countTimes = countTimes; stateQueue.push(tmpState); } } } } int calculateMinTimes(queue<LeftRiverSideState> &stateQueue, vector<BoatState> &boatStates) { vector<LeftRiverSideState> checkState; while(stateQueue.size() > 0) { LeftRiverSideState lrsState = stateQueue.front(); stateQueue.pop(); //end condition if(lrsState.cannibals==0 && lrsState.scholars==0 && lrsState.isBoatOnStandby == false) { return lrsState.countTimes; } //check left/right river side if( (lrsState.cannibals>lrsState.scholars && lrsState.scholars!=0) || (N-lrsState.cannibals>N-lrsState.scholars && N-lrsState.scholars!=0) ) { continue; } //check endless loop bool flag = false; for(vector<LeftRiverSideState>::iterator iter=checkState.begin(); iter!=checkState.end(); ++iter) { if(iter->cannibals==lrsState.cannibals && iter->isBoatOnStandby==lrsState.isBoatOnStandby && iter->scholars==lrsState.scholars) { flag = true; break; } } if(flag) { continue; } checkState.push_back(lrsState); changeState(lrsState, stateQueue, boatStates); } return -1; } int main(int argc, char** argv) { int tc, T; freopen("E:/sample_input.txt", "r", stdin); cin >> T; for(tc = 0; tc < T; tc++) { cin >> N >> M; vector<BoatState> boatStates; createBoatState(boatStates); LeftRiverSideState lrsState; lrsState.cannibals = N; lrsState.scholars = N; lrsState.countTimes = 0; lrsState.isBoatOnStandby = true; queue<LeftRiverSideState> stateQueue; stateQueue.push(lrsState); int ret = calculateMinTimes(stateQueue, boatStates); if(ret != -1) { cout << ret << endl; } else { cout << "impossible" <<endl; } } return 0; }