人狼羊菜过河问题

人狼羊菜过河问题:

PTA原题:

image-20201119222220215

输入:

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;
}
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值