人狼羊菜过河问题:
PTA原题:
输入:
MWGC -> ....
.... <- MWGC
输出:
MWGC -> ....
.W.C <- M.G.
MW.C -> ..G.
...C <- MWG.
M.GC -> .W..
..G. <- MW.C
M.G. -> .W.C
.... <- MWGC
MWGC -> ....
.W.C <- M.G.
MW.C -> ..G.
.W.. <- M.GC
MWG. -> ...C
..G. <- MW.C
M.G. -> .W.C
.... <- MWGC
输入:
.W.. <- M.GC
M.GC -> .W..
输出:
.W.. <- M.GC
MWG. -> ...C
..G. <- MW.C
M.GC -> .W..
.W.. <- M.GC
MW.C -> ..G.
...C <- MWG.
M.GC -> .W..
分析
这题本质上还是用 DFS+剪枝 暴力搜索所有状态, 以找到满足条件的解
这题我写了将近5个小时😂, 个人认为主要的难点和Debug点是在状态变化上, 本题中状态总共有8种:
/*
* 农夫单独过河
* 农夫带狼过河;
* 农夫带羊过河;
* 农夫带菜过河;
* 农夫单独返回;
* 农夫带狼返回;
* 农夫带羊返回;
* 农夫带菜返回;
*/
所以需要开一个八叉树进行DFS搜索, 其中需要对无效状态和重复状态进行剪枝, 并在状态符合最终结果的时候打印返回
首先是状态的转换, 用bool[8]
储存当前状态:
//输入并转化状态:
char temp[MAX]={0};
for(int i=0;i<4;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?startState[i]=false:startState[i]=true;
}
scanf("%*4c");
for(int i=4;i<8;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?startState[i]=false:startState[i]=true;
}
getchar();
for(int i=0;i<4;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?endState[i]=false:endState[i]=true;
}
scanf("%*4c");
for(int i=4;i<8;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?endState[i]=false:endState[i]=true;
}
getchar();
而后是八种状态的变换, 这里使用了Lambda表达式进行, 以便于在DFS时使用变量控制
当能够改变状态时返回true, 不能时返回false, 以便于DFS中进行判断
//定义8种状态:
/*
* 农夫单独过河
* 农夫带狼过河;
* 农夫带羊过河;
* 农夫带菜过河;
* 农夫单独返回;
* 农夫带狼返回;
* 农夫带羊返回;
* 农夫带菜返回;
*/
state[0]=[](void)->bool{
if(!nowState[0]){
return false;
}else if((nowState[1] && nowState[2]) ||
(nowState[2] && nowState[3])){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
return true;
}
};
state[1]=[](void)->bool{
if((!nowState[0]) || (!nowState[1])){
return false;
}else if(nowState[2] && nowState[3]){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
nowState[1]=false;
nowState[5]=true;
return true;
}
};
state[2]=[](void)->bool{
if((!nowState[0]) || (!nowState[2])){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
nowState[2]=false;
nowState[6]=true;
return true;
}
};
state[3]=[](void)->bool{
if((!nowState[0]) || (!nowState[3])){
return false;
}else if(nowState[1] && nowState[2]){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
nowState[3]=false;
nowState[7]=true;
return true;
}
};
//--------------------------------
state[4]=[](void)->bool{
if(!nowState[4]){
return false;
}else if((nowState[5] && nowState[6]) ||
(nowState[6 && nowState[7]])){
return false;
}else{
nowState[4]=false;
nowState[0]=true;
return true;
}
};
state[5]=[](void)->bool{
if((!nowState[4]) || (!nowState[5])){
return false;
}else if(nowState[6] && nowState[7]){
return false;
}else{
nowState[4]=false;
nowState[0]=true;
nowState[5]=false;
nowState[1]=true;
return true;
}
};
state[6]=[](void)->bool{
if((!nowState[4]) || (!nowState[6])){
return false;
}else {
nowState[4]=false;
nowState[0]=true;
nowState[6]=false;
nowState[2]=true;
return true;
}
};
state[7]=[](void)->bool{
if((!nowState[4]) || (!nowState[7])){
return false;
}else if(nowState[5] && nowState[6]){
return false;
}else{
nowState[4]=false;
nowState[0]=true;
nowState[7]=false;
nowState[3]=true;
return true;
}
};
判定状态是否重复这里使用了set
当有重复元素插入时, insert会返回一个false, 此时在DFS中进行剪枝:
bool isRepeat(bool s[]){
auto re=stateList.insert (stateToInt (s));
if(!re.second){
return true;
}else{
return false;
}
}
最后是DFS暴力搜索
这里debug了很久, 主要是忘了在return之后对当前状态进行回退, 导致set中状态没有清干净, 忽略了部分情况
void dfs(int stateNum){
for(int i=0;i<8;++i){
nowState[i]=stateTrace[tracePtr-1][i];
}
//改变状态, 如果状态非法, 则返回
if(!state[stateNum]()){
return ;
}else if(!isRepeat(nowState)){
pushState (nowState);
// printState ();
}else {
//如果状态重复, 则返回
return ;
}
//如果状态相等, 则打印返回
if(stateEqual (nowState, endState)){
if(ansCount!=0){
printf("\n");
}
printState ();
++ansCount;
popState ();
stateList.erase (stateToInt (nowState));
return ;
}
//递归
for(int i=0;i<8;++i){
if(tracePtr==3 && i==3){
// printf("breakP\n");
}
dfs(i);
}
for(int i=0;i<8;++i){
nowState[i]=stateTrace[tracePtr-1][i];
}
popState ();
stateList.erase (stateToInt (nowState));
return ;
}
源码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<bits/stdc++.h>
#include <queue>
#include <vector>
#include <utility>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
const int MAX=20+5;
bool (*state[8])(void);
//MWGC
bool nowState[8];
bool startState[8];
bool endState[8];
set<int> stateList;
int ansCount=0;
bool stateEqual(bool b1[], bool b2[]){
bool flag=true;
for(int i=0;i<8;++i){
if(b1[i]!= b2[i]){
flag=false;
break;
}
}
return flag;
}
int stateToInt(bool s[]){
int stateInt=0;
for(int i=0;i<8;++i){
if(s[i]){
stateInt+=pow(2,7-i);
}
}
return stateInt;
}
bool isRepeat(bool s[]){
auto re=stateList.insert (stateToInt (s));
if(!re.second){
return true;
}else{
return false;
}
}
int tracePtr=0;
//最后一位为stateNum
bool stateTrace[MAX][9]={0};
char stateFlag[]={"MWGCMWGC"};
void pushState(bool b[]){
for(int i=0;i<8;++i){
stateTrace[tracePtr][i]=b[i];
}
++tracePtr;
return ;
}
void popState(){
--tracePtr;
}
void printState(){
for(int i=0;i<tracePtr;++i){
for(int j=0;j<4;++j){
stateTrace[i][j]?printf("%c",stateFlag[j]):printf(".");
}
stateTrace[i][0]?printf(" -> "):printf(" <- ");
for(int j=4;j<8;++j){
stateTrace[i][j]?printf("%c",stateFlag[j]):printf(".");
}
putchar('\n');
}
// putchar('\n');
}
void dfs(int stateNum){
for(int i=0;i<8;++i){
nowState[i]=stateTrace[tracePtr-1][i];
}
//改变状态, 如果状态非法, 则返回
if(!state[stateNum]()){
return ;
}else if(!isRepeat(nowState)){
pushState (nowState);
// printState ();
}else {
//如果状态重复, 则返回
return ;
}
//如果状态相等, 则打印返回
if(stateEqual (nowState, endState)){
if(ansCount!=0)
printf("\n");
}
printState ();
++ansCount;
popState ();
stateList.erase (stateToInt (nowState));
return ;
}
//递归
for(int i=0;i<8;++i){
if(tracePtr==3 && i==3){
// printf("breakP\n");
}
dfs(i);
}
//状态回退
for(int i=0;i<8;++i){
nowState[i]=stateTrace[tracePtr-1][i];
}
popState ();
stateList.erase (stateToInt (nowState));
return ;
}
int main() {
//定义8种状态:
/*
* 农夫单独过河
* 农夫带狼过河;
* 农夫带羊过河;
* 农夫带菜过河;
* 农夫单独返回;
* 农夫带狼返回;
* 农夫带羊返回;
* 农夫带菜返回;
*/
state[0]=[](void)->bool{
if(!nowState[0]){
return false;
}else if((nowState[1] && nowState[2]) ||
(nowState[2] && nowState[3])){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
return true;
}
};
state[1]=[](void)->bool{
if((!nowState[0]) || (!nowState[1])){
return false;
}else if(nowState[2] && nowState[3]){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
nowState[1]=false;
nowState[5]=true;
return true;
}
};
state[2]=[](void)->bool{
if((!nowState[0]) || (!nowState[2])){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
nowState[2]=false;
nowState[6]=true;
return true;
}
};
state[3]=[](void)->bool{
if((!nowState[0]) || (!nowState[3])){
return false;
}else if(nowState[1] && nowState[2]){
return false;
}else{
nowState[0]=false;
nowState[4]=true;
nowState[3]=false;
nowState[7]=true;
return true;
}
};
//--------------------------------
state[4]=[](void)->bool{
if(!nowState[4]){
return false;
}else if((nowState[5] && nowState[6]) ||
(nowState[6 && nowState[7]])){
return false;
}else{
nowState[4]=false;
nowState[0]=true;
return true;
}
};
state[5]=[](void)->bool{
if((!nowState[4]) || (!nowState[5])){
return false;
}else if(nowState[6] && nowState[7]){
return false;
}else{
nowState[4]=false;
nowState[0]=true;
nowState[5]=false;
nowState[1]=true;
return true;
}
};
state[6]=[](void)->bool{
if((!nowState[4]) || (!nowState[6])){
return false;
}else {
nowState[4]=false;
nowState[0]=true;
nowState[6]=false;
nowState[2]=true;
return true;
}
};
state[7]=[](void)->bool{
if((!nowState[4]) || (!nowState[7])){
return false;
}else if(nowState[5] && nowState[6]){
return false;
}else{
nowState[4]=false;
nowState[0]=true;
nowState[7]=false;
nowState[3]=true;
return true;
}
};
//输入并转化状态:
char temp[MAX]={0};
for(int i=0;i<4;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?startState[i]=false:startState[i]=true;
}
scanf("%*4c");
for(int i=4;i<8;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?startState[i]=false:startState[i]=true;
}
getchar();
for(int i=0;i<4;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?endState[i]=false:endState[i]=true;
}
scanf("%*4c");
for(int i=4;i<8;++i){
scanf("%c",&temp[i]);
temp[i]=='.'?endState[i]=false:endState[i]=true;
}
getchar();
// for(int i=0;i<8;++i){
// printf("%d",startState[i]);
// }
// putchar('\n');
// for(int i=0;i<8;++i){
// printf("%d",endState[i]);
// }
for(int i=0;i<8;++i){
nowState[i]=startState[i];
}
pushState (nowState);
isRepeat (nowState);
for(int i=0;i<8;++i){
dfs(i);
}
// cout<<stateToInt (nowState)<<endl;
// cout<<stateToInt (endState)<<endl;
// cout<<isRepeat (startState)<<endl;
// cout<<isRepeat (endState)<<endl;
// pushState (startState);
// pushState (endState);
// printState ();
if(ansCount==0){
printf("None\n");
}
return 0;
}