【题目大意】
开始时有两个变量(A=x^a,B=x^b),每次可以做以下爱几种运算:
A*B,A*A,B*B,A/B,B/A,A/A,B/B
并把结果放到某个变量中,问最少多少步可以得到目标X^P
【题目分析】
我们可以只记录当前两个变量的指数,这样各种操作就变成了加和减,可以直接进行BFS,但是显然会超时,于是我们先从状态入手,规定a<=b,这样就把状态数减少了一半,而操作中的除法也就只剩下b/a这一种情况了,再继续考虑优化。
对于各种操作,其实A*A这个变化时与一些操作重复的,所以可以去掉这个变换,这样就减少了搜索树结点的度,使能达到的节点数更少了。
状态树上已经不能再优化了,继续考虑较少扩展量,有如下两个剪枝
①如果p mod gcd(A,B)<>0 那么状态<A,B>一定必能出解
②如果A>=100且B>=50000那么该状态无解
以上两个剪枝笔者都不会证明。。。。
以上两个剪枝加入后程序性能有所改善,但是仍然不能在时间限制内出解。我们只能考虑搜索扩展规则上的优化,使用A*算法。
介绍一下A*算法,给每一个状态一个估价值,表示该状态达到目标的期望水平,函数值越大,则该节点越有可能达到解,因此,我们在进行A*算法时需要维护两个表,表一代替了朴素BFS中的队列,能够在较短时间内取出并删除函数值最大的结点,还能在较短时间内插入新节点,而第二个表则是用来判重的,能够支持插入节点和查询节点是否已经存在,对于数据结构了解比较清楚的同学可能已经知道他们应该用什么实现了,表一用一个二叉堆来实现,在logn时间内完成插入操作或取出最小值并维护形态,表二则用一个平衡树或hash表来实现,个人推荐平衡树,比较稳定,不会像hash那样出问题。这样,A*的数据结构就选好了。
接下来确定的是估价函数,最快情况下变量按2的指数级别增长,所以定义H(state)为变量B不断自乘2最少多少次能大于等于P,定义G(state)为该节点的深度,定义估价函数F(state)=G(state)*2+H(state)
这里给G(state)乘2是为了保证估价函数的相容性,即保证先扩展出来的结点的步数一定是最优的,这样才能得到最优解。同时,利用刚刚的朴素BFS程序,我们还可以得到数据范围内的最大步数大概在20左右,所以如果某结点函数F(state)>20*2,那么我们完全可以舍弃这个节点。
如果还是不能出解,我们就要考虑牺牲解的正确性了,我的做法是在数据较强时估价函数G不乘2,这样时间大幅下降。
这样加入所有的优化后,程序效率明显上升,能够在较短时间内出解。而且结点总数在20万以下,可以说已经优化的很好了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 {$inline on} 2 program usaco_power(input,output); 3 const 4 maxpower= 200000; 5 type 6 nodes=record 7 x,y:longint; 8 end; 9 size_balance_tree=object 10 root,tot:longint; 11 s,left,right:array[0..200000] of longint; 12 key:array[0..300000] of nodes; 13 procedure clean; 14 procedure left_rotate(var t:longint); 15 procedure right_rotate(var t:longint); 16 procedure maintain(var t:longint;flag:boolean); 17 procedure insert(var t:longint;xx:nodes); 18 function find(t:longint;xx:nodes):boolean; 19 end; 20 node=record 21 x,y,w,step:longint; 22 end; 23 Theap=object 24 tot:longint; 25 list:array[0..100000] of node; 26 procedure clean; 27 procedure swap(var aa,bb:longint); 28 procedure up(now:longint); 29 procedure down(now:longint); 30 procedure insert(now:node); 31 function delete():node; 32 end; 33 procedure size_balance_tree.clean(); 34 begin 35 root:=0; 36 tot:=0; 37 fillchar(left,sizeof(left),0); 38 fillchar(right,sizeof(right),0); 39 fillchar(s,sizeof(s),0); 40 fillchar(key,sizeof(key),0); 41 end; 42 procedure size_balance_tree.left_rotate(var t:longint); 43 var 44 k:longint; 45 begin 46 k:=right[t]; 47 right[t]:=left[k]; 48 left[k]:=t; 49 s[k]:=s[t]; 50 s[t]:=s[left[t]]+s[right[t]]+1; 51 t:=k; 52 end; 53 procedure size_balance_tree.right_rotate(var t:longint); 54 var 55 k:longint; 56 begin 57 k:=left[t]; 58 left[t]:=right[k]; 59 right[k]:=t; 60 s[k]:=s[t]; 61 s[t]:=s[left[t]]+s[right[t]]+1; 62 t:=k; 63 end; 64 procedure size_balance_tree.maintain(var t:longint;flag:boolean); 65 begin 66 if not flag then 67 begin 68 if s[left[left[t]]]>s[right[t]] then 69 size_balance_tree.right_rotate(t) 70 else 71 if s[right[left[t]]]>s[right[t]] then 72 begin 73 size_balance_tree.left_rotate(left[t]); 74 size_balance_tree.right_rotate(t); 75 end 76 else 77 exit; 78 end 79 else 80 begin 81 if s[right[right[t]]]>s[left[t]] then 82 size_balance_tree.left_rotate(t) 83 else 84 if s[left[right[t]]]>s[left[t]] then 85 begin 86 size_balance_tree.right_rotate(right[t]); 87 size_balance_tree.left_rotate(t); 88 end 89 else 90 exit; 91 end; 92 size_balance_tree.maintain(left[t],false); 93 size_balance_tree.maintain(right[t],true); 94 size_balance_tree.maintain(t,false); 95 size_balance_tree.maintain(t,true); 96 end; 97 procedure size_balance_tree.insert(var t:longint;xx:nodes); 98 begin 99 if t=0 then 100 begin 101 inc(tot); 102 t:=tot; 103 left[t]:=0; 104 right[t]:=0; 105 s[t]:=1; 106 key[t]:=xx; 107 end 108 else 109 begin 110 inc(s[t]); 111 if (xx.x*maxpower+xx.y)<(key[t].x*maxpower+key[t].y) then 112 size_balance_tree.insert(left[t],xx) 113 else 114 size_balance_tree.insert(right[t],xx); 115 size_balance_tree.maintain(t,xx.x*maxpower+xx.y>=key[t].x*maxpower+key[t].y); 116 end; 117 end; 118 function size_balance_tree.find(t:longint;xx:nodes):boolean; 119 begin 120 if t=0 then 121 exit(false); 122 if (key[t].x=xx.x)and(key[t].y=xx.y) then 123 exit(true); 124 if (xx.x*maxpower+xx.y)<(key[t].x*maxpower+key[t].y) then 125 exit(size_balance_tree.find(left[t],xx)) 126 else 127 exit(size_balance_tree.find(right[t],xx)); 128 end; 129 130 131 procedure Theap.clean(); 132 begin 133 fillchar(list,sizeof(list),0); 134 tot:=0; 135 end; 136 procedure Theap.swap(var aa,bb:longint); 137 var 138 tt:longint; 139 begin 140 tt:=aa; 141 aa:=bb; 142 bb:=tt; 143 end; 144 procedure Theap.up(now:longint); 145 begin 146 while (now>1)and(list[now].w<list[now>>1].w) do 147 begin 148 Theap.swap(list[now].x,list[now>>1].x); 149 Theap.swap(list[now].y,list[now>>1].y); 150 Theap.swap(list[now].w,list[now>>1].w); 151 Theap.swap(list[now].step,list[now>>1].step); 152 now:=now>>1; 153 end; 154 end; 155 procedure Theap.down(now:longint); 156 var 157 tmp:longint; 158 begin 159 while (now*2<=tot) do 160 begin 161 tmp:=now*2; 162 if (tmp+1<=tot)and(list[tmp+1].w<list[tmp].w) then 163 inc(tmp); 164 if list[tmp].w<list[now].w then 165 begin 166 Theap.swap(list[now].x,list[tmp].x); 167 Theap.swap(list[now].y,list[tmp].y); 168 Theap.swap(list[now].w,list[tmp].w); 169 Theap.swap(list[now].step,list[tmp].step); 170 now:=tmp; 171 end 172 else 173 break; 174 end; 175 end; 176 procedure Theap.insert(now:node); 177 begin 178 inc(tot); 179 list[tot]:=now; 180 Theap.up(tot); 181 end; 182 function Theap.delete():node; 183 begin 184 delete:=list[1]; 185 Theap.swap(list[1].x,list[tot].x); 186 Theap.swap(list[1].y,list[tot].y); 187 Theap.swap(list[1].w,list[tot].w); 188 Theap.swap(list[1].step,list[tot].step); 189 dec(tot); 190 Theap.down(1); 191 end; 192 193 var 194 tree:size_balance_tree; 195 heap:Theap; 196 goal,answer:longint; 197 answer_flag,check_goal:boolean; 198 function gcd(a,b:longint):longint; inline; 199 begin 200 if b=0 then 201 exit(a); 202 exit(gcd(b,a mod b)); 203 end;{ gcd } 204 procedure check(); 205 var 206 i,s:longint; 207 begin; 208 s:=0; 209 for i:=0 to 30 do 210 if ((1<<i) and goal)>0 then 211 inc(s); 212 if s<=9 then 213 check_goal:=true 214 else 215 check_goal:=false; 216 end; 217 function f(now:node):longint; inline; 218 var 219 tmp:longint; 220 begin 221 f:=0; 222 tmp:=now.y; 223 if tmp=0 then 224 exit(maxlongint>>1); 225 while tmp<goal do 226 begin 227 tmp:=tmp+tmp; 228 inc(f); 229 end; 230 if (check_goal)and(goal>10000) then 231 f:=f+now.step 232 else 233 f:=f+now.step<<1; 234 end;{ f } 235 procedure swap(var aa,bb:longint); inline; 236 var 237 tt:longint; 238 begin 239 tt:=aa; 240 aa:=bb; 241 bb:=tt; 242 end;{ swap } 243 procedure init; inline; 244 var 245 now:node; 246 new:nodes; 247 begin 248 readln(goal); 249 check(); 250 tree.clean(); 251 heap.clean(); 252 now.x:=0; 253 now.y:=1; 254 now.step:=0; 255 now.w:=f(now); 256 new.x:=0; 257 new.y:=1; 258 tree.insert(tree.root,new); 259 heap.insert(now); 260 end;{ init } 261 function operand(now:node;flag:longint):node; inline; 262 begin 263 case flag of 264 1:begin 265 operand.x:=now.x; 266 operand.y:=now.x+now.y; 267 operand.step:=now.step+1; 268 end; 269 2:begin 270 operand.x:=now.x+now.y; 271 operand.y:=now.y; 272 operand.step:=now.step+1; 273 end; 274 3:begin 275 operand.x:=now.y-now.x; 276 operand.y:=now.y; 277 operand.step:=now.step+1; 278 end; 279 4:begin 280 operand.x:=now.x; 281 operand.y:=now.y-now.x; 282 operand.step:=now.step+1; 283 end; 284 {5:begin 285 operand.x:=now.x+now.x; 286 operand.y:=now.y; 287 operand.step:=now.step+1; 288 end; 289 6:begin 290 operand.x:=now.x; 291 operand.y:=now.x+now.x; 292 operand.step:=now.step+1; 293 end;} 294 5:begin 295 operand.x:=now.y+now.y; 296 operand.y:=now.y; 297 operand.step:=now.step+1; 298 end; 299 6:begin 300 operand.x:=now.x; 301 operand.y:=now.y+now.y; 302 operand.step:=now.step+1; 303 end; 304 end; 305 if operand.y<operand.x then 306 swap(operand.x,operand.y); 307 end;{ operand } 308 procedure expand(now:node); inline; 309 var 310 newstate:node; 311 pd:nodes; 312 i:longint; 313 begin 314 for i:=1 to 6 do 315 begin 316 newstate:=operand(now,i); 317 if not ((newstate.x<=100)and(newstate.y<=50000)) then 318 continue; 319 if newstate.y=0 then 320 continue; 321 if (goal mod gcd(newstate.x,newstate.y))<>0 then 322 continue; 323 if (newstate.x=goal)or(newstate.y=goal) then 324 begin 325 answer_flag:=true; 326 answer:=newstate.step; 327 exit; 328 end; 329 newstate.w:=f(newstate); 330 if newstate.w>40 then 331 continue; 332 pd.x:=newstate.x; 333 pd.y:=newstate.y; 334 if tree.find(tree.root,pd) then 335 continue; 336 tree.insert(tree.root,pd); 337 heap.insert(newstate); 338 end; 339 end;{ expand } 340 procedure A_star; inline; 341 var 342 state:node; 343 begin 344 answer_flag:=false; 345 while heap.tot<>0 do 346 begin 347 state:=heap.delete(); 348 expand(state); 349 if answer_flag then 350 break; 351 end; 352 end;{ A_star } 353 procedure print; inline; 354 begin 355 writeln(answer); 356 end;{ print } 357 begin 358 init; 359 A_star; 360 print; 361 end.