题意:每个人只能进行一种操作,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;
}