仅供学习使用,还请大神多多指点!
传教士与野人
问题描述:M个传教士和N个野人在河的一边,还有一条能载一个人或者两个人的船。找到一个办法让所有的人都渡到河另一岸,要求在任何地方野人数都不能多于传教士的人数(可以只有野人没有传教士)。
问题形式化:1、状态:每个状态有河两岸A与B对应的传教士与野人数和船的位置决定。
2、初始状态:A点M个野人N个传教士船在A侧。
3、后继函数:返回状态取决于船的方向与船在的人
4、目标测试:M个野人和N个传教士都在B侧
5、路径耗散:船渡河的次数
程序设计:1、状态保存在长度为2的数组state[2]中,s[0]与s[1]分别为A侧野人与传教士数量,和一个状态标志表示船的位置。 如何压缩状态?
2、状态转移函数取决于船的方向,船的共有十种方向保存在数组dir[10]中,dir[0]至dir[4]分别表示船从A到B运送一名传教士、一名野人、一名传教士和一名野人、两名传教士、两名野人,同样dir[5]至dir[9]分别表示从B到A运送一名传教士、一名野人、一名传教士和一名野人、两名传教士、两名野人。
3、目标检测函数check()检查目前状态是否已经为目标状态。
源程序:
1 #include2 #include 3 #include 4 #include 5 #include 6 7 #define MAXN 1000 8 9 using namespace std; 10 11 struct STATE 12 { 13 int missionary; 14 int savage; 15 int boat; //标识船的地点,0表示在左侧,1表示在右侧 16 17 STATE(int m,int s,int b) 18 { 19 missionary = m; 20 savage = s; 21 boat = b; 22 } 23 }; 24 25 int pre[MAXN]; 26 int dir[10][2] = {{-2,0},{-1,-1},{-1,0},{0,-2},{0,-1},{2,0},{1,1},{1,0},{0,2},{0,1}}; 27 28 int bfs(int num_m,int num_s); 29 int get_state(STATE state); 30 bool check(int cur_missionary,int cur_savage,int all_missionary,int all_savage,int next_state); 31 void trace(int beg_state,int end_state); 32 33 int main() 34 { 35 int num_m,num_s; 36 37 cout << "please input the number of missionary and savage:" << endl; 38 39 while(cin >> num_m >> num_s) 40 { 41 if(bfs(num_m,num_s)) 42 { 43 STATE beg(num_m,num_s,0),end(0,0,1); 44 trace(get_state(beg),get_state(end)); 45 } 46 else 47 { 48 cout << "the problem is unsolvable!" << endl; 49 } 50 51 cout << endl << endl << "please input the number of missionary and savage:" << endl; 52 } 53 54 return 0; 55 } 56 57 int bfs(int num_m,int num_s) 58 { 59 STATE fir(num_m,num_s,0); 60 memset(pre,-1,sizeof(pre)); 61 pre[get_state(fir)] = -2; 62 63 queue q; 64 q.push(fir); 65 66 while(!q.empty()) 67 { 68 STATE cur = q.front(); 69 q.pop(); 70 71 if(!cur.missionary && !cur.savage) 72 { 73 return true; 74 } 75 76 for(int i = 0;i < 10;i++) 77 { 78 if(i < 5 && cur.boat || i >= 5 && !cur.boat) 79 { 80 continue; 81 } 82 STATE next = cur; 83 next.boat = !cur.boat; 84 next.missionary += dir[i][0]; 85 next.savage += dir[i][1]; 86 int next_state = get_state(next); 87 88 if(check(next.missionary,next.savage,num_m,num_s,next_state)) 89 { 90 continue; 91 } 92 // cout << next.missionary << next.savage << next.boat << endl; 93 q.push(next); 94 pre[next_state] = i; //记录方向 95 } 96 } 97 98 return false; 99 } 100 101 //只是简单的把三个状态压缩到一个十进制数中,暂时没有想到更好的压缩方式, 102 //因此暂时传教士数与野人数只能小于10. 103 104 int get_state(STATE state) 105 { 106 return state.missionary * 100 + state.savage * 10 + state.boat; 107 } 108 109 bool check(int cur_missionary,int cur_savage,int all_missionary,int all_savage,int next_state) 110 { 111 if(cur_missionary < 0 || cur_missionary > all_missionary || cur_savage < 0 || cur_savage > all_savage) //检查是否越界 112 { 113 return true; 114 } 115 116 if((cur_missionary && cur_missionary < cur_savage) || ((all_missionary - cur_missionary) && all_missionary - cur_missionary < all_savage - cur_savage)) //检查是否满足两岸传教士人数不少于野人数 117 { 118 return true; 119 } 120 121 if(pre[next_state] != -1) //检查是否已经搜索过此状态 122 { 123 return true; 124 } 125 126 return false; 127 } 128 129 void trace(int beg_state,int end_state) 130 { 131 stack<int> path; 132 int state = end_state; 133 134 while(state != beg_state) //写的好乱 135 { 136 int m = state / 100; 137 int s = (state / 10) % 10; 138 int b = state % 10; 139 int d = pre[state]; 140 path.push(d); 141 m -= dir[d][0]; 142 s -= dir[d][1]; 143 b = !b; 144 state = get_state(STATE(m,s,b)); 145 } 146 147 while(!path.empty()) 148 { 149 int d = path.top(); 150 path.pop(); 151 if(d < 5) 152 cout << "move" << setw(10) << -dir[d][0] << " missionary and" << setw(10) << -dir[d][1] << " savage from A sides to B sides" << endl; 153 else 154 cout << "move" << setw(10) << dir[d][0] << " missionary and" << setw(10) << dir[d][1] << " savage from B sides to A sides" << endl; 155 } 156 }