跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。 写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。
【题解】
在某一种状态下只有三种跳法,中间跳左边,中间跳右边,左右(离中间更近的一个)向中间跳,以当前状态下abc为节点建树,向中间跳即为当前节点的父节点,向两边跳即为当前节点的子节点,然后就是求树上两个节点间最短路的问题
1.无解当且仅当初始/末状态对应的树的根节点所表示的状态不同
2.易知从当前节点往上的(t1-1)/t2 代父节点都表示从相同的一侧往中间跳(t1为前两点/后两点之差中的较大值,t2为较小值),通过此方法可以在logk的时间内求出当前节点的弟k代祖先,然后再用一次ST算法即可。
*上跳时要注意在(t1-1)/t2大于当前节点的深度时的处理。
【代码】(无视int64其实根本不需要)
program chess;
type
state = record
a, b, c: int64;
end;
var
s, t: state;
s1, t1: state;
ans: int64;
da, db: int64;
i: longint;
function geth(a: state): int64;
var
i1, i2: int64;
l: int64;
begin
geth := 0;
i1 := a.b - a.a;
i2 := a.c - a.b;
while True do
begin
if i1 < i2 then
begin
l := (i2 - 1) div i1;
if l = 0 then
break;
geth := geth + l;
a.a := a.a + ((l + 1) div 2) * 2 * i1;
a.b := a.b + (l div 2) * 2 * i1;
if a.a > a.b then
begin
a.a := a.a + a.b;
a.b := a.a - a.b;
a.a := a.a - a.b;
end;
i1 := a.b - a.a;
i2 := a.c - a.b;
end
else
begin
l := (i1 - 1) div i2;
if l = 0 then
break;
geth := geth + l;
a.c := a.c - ((l + 1) div 2) * 2 * i2;
a.b := a.b - (l div 2) * 2 * i2;
if a.b > a.c then
begin
a.b := a.b + a.c;
a.c := a.b - a.c;
a.b := a.b - a.c;
end;
i1 := a.b - a.a;
i2 := a.c - a.b;
end;
end;
end;
function getf(a: state; b: int64): state;
var
i1, i2: int64;
l: int64;
begin
i1 := a.b - a.a;
i2 := a.c - a.b;
while True do
begin
if i1 < i2 then
begin
l := (i2 - 1) div i1;
if l = 0 then
break;
if l > b then
begin
l := b;
b := 0; //这句话不能丢..
end
else
b := b - l;
a.a := a.a + ((l + 1) div 2) * 2 * i1;
a.b := a.b + (l div 2) * 2 * i1;
if a.a > a.b then
begin
a.a := a.a + a.b;
a.b := a.a - a.b;
a.a := a.a - a.b;
end;
i1 := a.b - a.a;
i2 := a.c - a.b;
end
else
begin
l := (i1 - 1) div i2;
if l = 0 then
break;
if l > b then
begin
l := b;
b := 0;
end
else
b := b - l;
a.c := a.c - ((l + 1) div 2) * 2 * i2;
a.b := a.b - (l div 2) * 2 * i2;
if a.b > a.c then
begin
a.b := a.b + a.c;
a.c := a.b - a.c;
a.b := a.b - a.c;
end;
i1 := a.b - a.a;
i2 := a.c - a.b;
end;
if b = 0 then
break;
end;
if b = 0 then
getf := a
else
begin
getf.a := 0;
getf.b := 0;
getf.c := 0;
end;
end;
begin
readln(s.a, s.b, s.c);
readln(t.a, t.b, t.c);
ans := 0;
if s.a > s.b then
begin
s.a := s.a + s.b;
s.b := s.a - s.b;
s.a := s.a - s.b;
end;
if s.b > s.c then
begin
s.b := s.b + s.c;
s.c := s.b - s.c;
s.b := s.b - s.c;
end;
if s.a > s.b then
begin
s.a := s.a + s.b;
s.b := s.a - s.b;
s.a := s.a - s.b;
end;
if t.a > t.b then
begin
t.a := t.a + t.b;
t.b := t.a - t.b;
t.a := t.a - t.b;
end;
if t.b > t.c then
begin
t.b := t.b + t.c;
t.c := t.b - t.c;
t.b := t.b - t.c;
end;
if t.a > t.b then
begin
t.a := t.a + t.b;
t.b := t.a - t.b;
t.a := t.a - t.b;
end;
da := geth(s);
db := geth(t);
s1 := getf(s, da);
t1 := getf(t, db);
if (s1.a <> t1.a) or (s1.b <> t1.b) or (s1.c <> t1.c) then
begin
writeln('NO');
exit;
end
else
writeln('YES');
ans := ans + abs(da - db);
if da < db then
t := getf(t, db - da);
if da > db then
s := getf(s, da - db);
if da < db then
db := da
else
da := db;
for i := 30 downto 0 do
if 1 shl i <= da then
begin
s1 := getf(s, 1 shl i);
t1 := getf(t, 1 shl i);
if (s1.a <> t1.a) or (s1.b <> t1.b) or (s1.c <> t1.c) then
begin
s := s1;
t := t1;
ans := ans + 1 shl (i + 1);
end;
end;
if (s.a <> t.a) or (s.b <> t.b) or (s.c <> t.c) then
ans := ans + 2;
writeln(ans);
end.