蓝桥杯 ALGO-137 Lift and Throw

题意:每个人只能进行一种操作,1.移动;2.将某人举起;3.将某人抛出,求怎样才能达到最远的距离,

思路:此题需要全排列的问题,不懂的可以去百度一下,把每个人的动作映射的0-8的数字上面,i/3表示其人,i%3表示其执行的操作,对每种情况进行dfs(深搜)+回溯剪枝,大体思路是这样的。分不同的动作执行不同的操作。

移动和抛出的情况大体差不多,主要先看后面有没有人,有的话就从其前面的一个位置进行搜索,搜索之后再进行回溯。修改节点的状态需要注意下,因为容易漏而导致其后面结果出错。

思路来源:https://blog.csdn.net/yoer77/article/details/53707149

代码:

#include <iostream>
#include<algorithm>
#define N 100
using namespace std; 
struct node{
	int pos,lift,maxMove,maxThrow;
	bool lifting,lifted,hasMove,hasLift,hasThrow;
}p[3];
bool Pos[N],visit[N];//位置数组,访问数组 
int ans = 0;//保存答案 
void dfs(int k,int step){//k是何种操作,step进行到几步了 
	int n = k/3;//执行该操作的人 
	int m = k%3;//执行的什么动作 
	if(m == 0){//执行的是移动操作,如果已经移动,或者正在被举起或者已经举起了别则不能移动了 
		if(p[n].hasMove || p[n].lifted || p[n].lifting) return;
		int i = 1;//下次移动的距离
		if(step == 9) i = p[n].maxMove;//如果递归的层次已经为最后一层了,则直接为最大的移动距离
		else{//不是的话,从后面的点开始搜索,如果后面没有点了,直接往前面搜索 
			for(int j=1;j<p[n].pos;j++){
				if(Pos[j]){//后面有人才会从后面检查 
					int l = -(p[n].pos - j - 1);//标识后面点的位置
					i = min(i,l);//后面有点的话l就是一个负数,如果没有点的话直接往前面开始搜索 
				}
			}
			i = max(i,-p[n].maxMove);//移动的最大距离不能超过其最远的距离,政府统一了 
		} 
		for(i;i<=p[n].maxMove;i++){
			if(Pos[p[n].pos+i+1]||Pos[p[n].pos+i-1]||i==p[n].maxMove){//搜索的前面位置或者后面位置有人或者最后一个了 
				if(p[n].pos+i>0 && Pos[p[n].pos+i]==false){//移动的位置合法且没有人占用当前的位置
					if(i==0)continue; 
					Pos[p[n].pos] = false;//将移动前的位置进行回收
					p[n].pos += i;//移动当前的位置 
					Pos[p[n].pos] = true;//将移动后的位置进行设置
					p[n].hasMove = true;
  					ans = max(ans,p[n].pos);//更新其最远距离 
					
					for(int j=0;j<9;j++){//从没有执行过的数组里面再次进行dfs 
						if(!visit[j]){
							visit[j] = true;
							dfs(j,step+1);
							visit[j] = false; 
						}
					}
					p[n].hasMove = false;
					Pos[p[n].pos] = false;//回溯 
					p[n].pos -= i;
					Pos[p[n].pos] = true; 
				}
			} 
		} 
	}else if(m == 1){//举起的动作 
		if(p[n].lifting || p[n].lifted || p[n].hasLift)return;//正在被别人举起或者正在举起别人或者已经举起过了别人,则不执行 
		for(int i=0;i<3;i++){
			if(abs(p[n].pos-p[i].pos)==1 && !p[i].lifted){//位置相隔1且位置上面有人,且没人将其举起 
				p[n].hasLift = true;
				p[n].lifting = true;//修改n状态 
				p[n].lift = i;
				p[i].lifted = true;//修改i的状态
				int tem =  p[i].pos;//暂存其i的位置
				Pos[p[i].pos] = false;
				p[i].pos = p[n].pos;
				if(p[i].lifting){//如果此时i也在举起别人,那么一同举起 
					int j = p[i].lift;
					p[j].pos = p[i].pos;
				}
				
				for(int j = 0;j<9;j++){//在对其他动作进行dfs 
					if(!visit[j]){
						visit[j] = true;
						dfs(j,step+1);
						visit[j] = false;
					}
				} 
				p[n].hasLift = false;
				p[n].lifting = false;
				p[n].lift = -1;
				p[i].lifted = false;
				p[i].pos = tem;
				Pos[p[i].pos] = true;	
				if(p[i].lifting){//回溯 
					int j = p[i].lift;
					p[j].pos = p[i].pos;
				}
			}
		} 
	}else{//抛出的动作 
		if(!p[n].lifting || p[n].lifted || p[n].hasThrow)return;//如果没有举起或则被举起或则已经抛出了
		int i = 1; 
		if(step == 9)i = p[n].maxThrow;
		else{
			for(int j=1;j<p[n].pos;j++){
				if(Pos[j]){
					int l = -(p[n].pos - j - 1);
					i = min(l,i);
				}
			}
			i = max(i,-p[n].maxThrow);
		}		
		for(i;i<=p[n].maxThrow;i++){
			if(Pos[p[n].pos+i-1] || Pos[p[n].pos+i+1] || i==p[n].maxThrow){
				if(Pos[p[n].pos+i] == false && p[n].pos+i>0){//位置合理且没有被别人占用 
					int j = p[n].lift;//暂存被举起的人
					p[j].pos += i; 
					p[n].lifting = false;
					p[n].lift = -1;
					p[j].lifted = false; 
					Pos[p[j].pos] = true;
					ans = max(p[j].pos,ans);
					if(p[j].lifting){
						int k = p[j].lift;
						p[k].pos = p[j].pos;
					}
					
					for(int q=0;q<9;q++){
						if(q==k)continue;
						if(!visit[q]){
							visit[q] = true;
							dfs(q,step+1);
							visit[q] = false;
						}
					}
					Pos[p[j].pos] = false;
					p[j].pos -= i;
					p[j].lifted = true;
					p[n].lift = j;
					p[n].lifting = true;
					if(p[j].lifting){
						int k = p[j].lift;
						p[k].pos = p[j].pos;
					}
				}
			}
		}
	}
}
int main(int argc, char** argv) {
	for(int i=0;i<3;i++){//输入数据 
		cin>>p[i].pos>>p[i].maxMove>>p[i].maxThrow;
		p[i].lifting = p[i].lifted = p[i].hasMove = p[i].hasLift = p[i].hasThrow = false;
		p[i].lift = -1;//设置举起的人为-1,因为有可能举起的人为0,所以不能为0 
		Pos[p[i].pos] = true;//设置该位置被占用了 
	}
	for(int i=0;i<9;i++){//对三个人的每个动作进行枚举 
		if(i%3!=2){//一开始不能抛,只能移动或者举起 
			visit[i] = true;
			dfs(i,1);
			visit[i] = false;//回溯	
		} 
	}
	cout<<ans; 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值