试题 算法训练 Lift and Throw
资源限制
时间限制:3.0s 内存限制:256.0MB
问题描述
给定一条标有整点(1, 2, 3, ...)的射线. 定义两个点之间的距离为其下标之差的绝对值.
Laharl, Etna, Flonne一开始在这条射线上不同的三个点, 他们希望其中某个人能够到达下标最大的点.
每个角色只能进行下面的3种操作, 且每种操作不能每人不能进行超过一次.
1.移动一定的距离
2.把另一个角色高举过头
3.将举在头上的角色扔出一段距离
每个角色有一个movement range参数, 他们只能移动到没有人的位置, 并且起点和终点的距离不超过movement range.
如果角色A和另一个角色B距离为1, 并且角色B没有被别的角色举起, 那么A就能举起B. 同时, B会移动到A的位置,B原来所占的位置变为没有人的位置. 被举起的角色不能进行任何操作, 举起别人的角色不能移动.同时, 每个角色还有一个throwing range参数, 即他能把举起的角色扔出的最远的距离. 注意, 一个角色只能被扔到没有别的角色占据的位置. 我们认为一个角色举起另一个同样举起一个角色的角色是允许的. 这种情况下会出现3个人在同一个位置的情况. 根据前面的描述, 这种情况下上面的两个角色不能进行任何操作, 而最下面的角色可以同时扔出上面的两个角色. 你的任务是计算这些角色能够到达的位置的最大下标, 即最大的数字x, 使得存在一个角色能够到达x.
Laharl, Etna, Flonne一开始在这条射线上不同的三个点, 他们希望其中某个人能够到达下标最大的点.
每个角色只能进行下面的3种操作, 且每种操作不能每人不能进行超过一次.
1.移动一定的距离
2.把另一个角色高举过头
3.将举在头上的角色扔出一段距离
每个角色有一个movement range参数, 他们只能移动到没有人的位置, 并且起点和终点的距离不超过movement range.
如果角色A和另一个角色B距离为1, 并且角色B没有被别的角色举起, 那么A就能举起B. 同时, B会移动到A的位置,B原来所占的位置变为没有人的位置. 被举起的角色不能进行任何操作, 举起别人的角色不能移动.同时, 每个角色还有一个throwing range参数, 即他能把举起的角色扔出的最远的距离. 注意, 一个角色只能被扔到没有别的角色占据的位置. 我们认为一个角色举起另一个同样举起一个角色的角色是允许的. 这种情况下会出现3个人在同一个位置的情况. 根据前面的描述, 这种情况下上面的两个角色不能进行任何操作, 而最下面的角色可以同时扔出上面的两个角色. 你的任务是计算这些角色能够到达的位置的最大下标, 即最大的数字x, 使得存在一个角色能够到达x.
输入格式
输入共三行, 分别为Laharl, Etna, Floone的信息.
每一行有且仅有3个整数, 描述对应角色的初始位置, movement range, throwing range.
数据保证3个角色的初始位置两两不相同且所有的数字都在1到10之间.
每一行有且仅有3个整数, 描述对应角色的初始位置, movement range, throwing range.
数据保证3个角色的初始位置两两不相同且所有的数字都在1到10之间.
输出格式
仅有1个整数, 即Laharl, Etna, Flonne之一能到达的最大距离.
样例输入
9 3 3
4 3 1
2 3 3
4 3 1
2 3 3
样例输出
15
样例说明
一开始Laharl在位置9, Etna在位置4, Flonne在位置2.
首先, Laharl移动到6.
然后Flonne移动到位置5并且举起Etna.
Laharl举起Flonne将其扔到位置9.
Flonne把Etna扔到位置12.
Etna移动到位置15.
首先, Laharl移动到6.
然后Flonne移动到位置5并且举起Etna.
Laharl举起Flonne将其扔到位置9.
Flonne把Etna扔到位置12.
Etna移动到位置15.
这题初看挺复杂,连要搜索什么都不知道,于是就百度了一下,发现切入点是 每个人可以有3次操作,那么一共就是9次操作,我们要深搜的就是这9次操作,其他要做的就是剪枝。
感谢这位博主,写得很详细,一点就通.
要注意的有以下几点:1. 从数据规模可知,最大距离小于50,那么坐标轴的数组大小开50就足够了;2. 9次操作的第一步应当是移动或举起,而不是抛出;3. 若当前是最后一步操作,那么直接移动至可移动的最大距离 或 抛出至可抛出的最大距离;4. 已移动过、正举着人、正被举着的人不能移动;已举起过、正举着人、正被举起的人不能举起别人;未举着人、正被举起的人不能进行抛出操作;5. 移动和抛出的目标位置只需考虑两种情况:其他人旁边的位置、可移动/可抛出的最远的位置;6. 若当前操作是移动或抛出,若当前执行操作的人后面有人,那么从此人(坐标轴最左边的人)右边第一个位置开始遍历;若后面无人,那么从自己右边第一个位置开始遍历;7. 目标位置必须确保 还在坐标轴上(即下标>0)且 目标位置无人;
代码有点长,不过其实大部分都是更新执行操作的人的状态以及回溯,着重要注意和理解的是剪枝,也就是查找目标和一些判断的条件.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <string> 6 #include <cmath> 7 #include <algorithm> 8 #define INF 0x3f3f3f3f 9 #define zero 1e-7 10 11 using namespace std; 12 typedef long long ll; 13 const ll mod=50000; 14 const ll max_n=2e5+7; 15 16 struct node {//0表示否,1表示是,-1表示没举着任何人 17 int pos;//当前所在位置 18 int maxMove;//可移动的最大距离 19 int maxThrow; //可扔出的最大距离 20 bool beup;//是否被举着 21 int upwho;//是否举着,举着谁 22 bool hasmove;//是否曾移动过 23 bool hasup;//是否曾举过(扔过) 24 }p[3];//三个人 25 26 int maxd=0; 27 bool axis[50]={0};//标记数轴上是否有人,0表示无 28 bool ops[10]={0};//9个操作全排列 29 30 void dfs(int k, int step) { 31 int n=k/3;//当前执行操作的人 32 int m=k%3;//当前执行的操作 33 if(!m) {//移动 34 if(p[n].beup || p[n].hasmove || p[n].upwho!=-1) return;//被举着、移动过、举着人的人不能移动 35 int l=1; 36 if(step==9) l=p[n].maxMove;//若当前是最后一步操作,则直接移动最大距离即可 37 else { 38 for(int i=1; i<p[n].pos; i++) { 39 if(axis[p[i].pos]) {//找到坐标轴上第一个人的位置 40 l=-(p[n].pos-i-1); 41 break; 42 } 43 } 44 l=max(l, -p[n].maxMove);//移动距离不能超过maxMove 45 } 46 for(int i=l; i<=p[n].maxMove; i++) { 47 if(axis[p[n].pos+i-1] || axis[p[n].pos+i+1] || i==p[n].maxMove) {//目标位置应是其他人的旁边的位置或者能移动的最大距离 48 if(p[n].pos+i>0 && !axis[p[n].pos+i]) {//确保在坐标轴范围内 且 目标位置无人 49 if(!i) continue;//目标位置不能是自己当前所在位置 50 //以下,修改移动后需要更新的值 51 axis[p[n].pos]=false; 52 p[n].pos+=i; 53 axis[p[n].pos]=true; 54 p[n].hasmove=true; 55 maxd=max(p[n].pos, maxd); 56 57 //继续下一步搜索 58 for(int j=0; j<9; j++) { 59 if(!ops[j]) { 60 ops[j]=true; 61 dfs(j, step+1); 62 ops[j]=false; 63 } 64 } 65 //回溯 66 p[n].hasmove=false; 67 axis[p[n].pos]=false; 68 p[n].pos-=i; 69 axis[p[n].pos]=true; 70 } 71 } 72 } 73 } 74 if(m==1) {//举起 75 if(p[n].upwho!=-1 || p[n].beup || p[n].hasup) return;//未举着人、被举着、举过人的人不能举起别人 76 for(int i=0; i<3; i++) {//枚举每个人的位置 77 if(abs(p[i].pos-p[n].pos)==1) {//如果刚好有人在这个人旁边 78 if(p[i].beup) continue;//i不能重复被举起 79 80 p[n].hasup=true; 81 p[n].upwho=i; 82 p[i].beup=true; 83 int temp=p[i].pos;//记录i的位置,以便回溯 84 axis[p[i].pos]=false; 85 p[i].pos=p[n].pos; 86 if(p[i].upwho!=-1) //若i举着人 87 p[p[i].upwho].pos=p[i].pos; 88 89 //继续下一步搜索 90 for(int j=0; j<9; j++) { 91 if(!ops[j]) { 92 ops[j]=true; 93 dfs(j, step+1); 94 ops[j]=false; 95 } 96 } 97 //回溯 98 p[n].hasup=false; 99 p[n].upwho=-1; 100 p[i].beup=false; 101 p[i].pos=temp; 102 axis[p[i].pos]=true; 103 if(p[i].upwho!=-1) 104 p[p[i].upwho].pos=p[i].pos; 105 } 106 } 107 } 108 if(m==2) {//抛出 109 if(p[n].upwho==-1 || p[n].beup) return;//若没有举起别人或正被举起,则不可抛出 110 int l=1; 111 if(step==9) l=p[n].maxThrow;//若当前为最后一步操作,则直接抛出到最远距离 112 else { 113 for(int i=1; i<p[n].pos; i++) { 114 if(axis[i]) { 115 l=-(p[n].pos-i-1); 116 break; 117 } 118 } 119 l=max(l, -p[n].maxThrow);//扔出距离不能超过maxThrow 120 } 121 for(int i=l; i<=p[n].maxThrow; i++) { 122 if(p[n].pos+i>0 && !axis[p[n].pos+i]) {//确保目标位置还在坐标轴范围内 且 目标位置无人 123 if(axis[p[n].pos+i-1] || axis[p[n].pos+i+1] || i==p[n].maxThrow) {//目标位置应是其他人旁边或可扔出的最大距离 124 125 int temp=p[n].upwho;//记录举着谁以便回溯 126 p[temp].pos+=i; 127 p[temp].beup=false; 128 p[n].upwho=-1; 129 p[n].hasup=true; 130 axis[p[temp].pos]=true; 131 maxd=max(maxd, p[temp].pos); 132 if(p[temp].upwho!=-1) 133 p[p[temp].upwho].pos=p[temp].pos; 134 135 //继续下一步搜索 136 for(int j=0; j<9; j++) { 137 if(!ops[j]) { 138 ops[j]=true; 139 dfs(j, step+1); 140 ops[j]=false; 141 } 142 } 143 //回溯 144 axis[p[temp].pos]=false; 145 p[temp].pos=p[n].pos; 146 p[temp].beup=true; 147 p[n].upwho=temp; 148 if(p[temp].upwho!=-1) 149 p[p[temp].upwho].pos=p[temp].pos; 150 } 151 } 152 } 153 } 154 } 155 156 int main() { 157 for(int i=0; i<3; i++) { 158 cin>>p[i].pos>>p[i].maxMove>>p[i].maxThrow; 159 p[i].beup=false; 160 p[i].upwho=-1; 161 p[i].hasmove=false; 162 p[i].hasup=false; 163 axis[p[i].pos]=true; 164 } 165 166 for(int i=0; i<9; i++) { 167 if(i%3!=2) {//第一步应是移动或举起 168 ops[i]=true; 169 dfs(i, 1); 170 ops[i]=false;//回溯 171 } 172 } 173 printf("%d\n", maxd); 174 return 0; 175 } 176 177 /* 178 9 3 3 179 4 3 1 180 2 3 3 181 182 15 183 */