【NOIP2016提高A组模拟8.15】Throw(BZOJ2144)

本文解析了一个涉及点操作的算法竞赛题目,通过构建树状模型来寻找最优解路径,并提出了一种高效算法,能够显著减少计算时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

这里写图片描述
Input

这里写图片描述
Sample Input

1 2 3
0 3 5
Output

这里写图片描述
Sample Output

YES
2

Data Constraint
这里写图片描述

题目大意

现在有A,B,C三个点,位置分别在x,y,z每一次我们可以进行下列的一项操作,将位置变为:
1:(x,x-(y-x),z)
2:(x,z+(z-y),z)
3:(y+(y-x),y,z)并且此时新的x位置要在z的前面,否则不能这样动
4:(x,y,z-(y-x))并且此时新的位置要在x的后面,否则不能这样动
现在给出初始位置和目标位置,求所需要的最短的步数(不一定要到对应的位置,只要xyz都到了三个目标位置的其中一个就可以了)

比赛时の想法

好难啊(╯‵□′)╯只会暴力

正解

这一题可以转化成一个树的模型并且求解,我们可以发现上面3,4两种转换方式只有一种是可行的,那么不妨把这种转换设为当前状态的父亲,同理,1,2两种状态设为当前状态的儿子。我们可以发现,对于任意一个三元组(x,y,z(x<y<z)),如果一直进行3,4操作,那么到最后一定会两种操作都进行不了(y-x=z-y)我们可以把这个状态设为这个三元组所在树的树根。当起始三元组和目标三元组到根都不相等时,那么这两个三元组就不在一棵树上,也就不可能转换为彼此了。
我们可以先把两个三元组都往根跑一遍,顺便得出深度,然后再二分一下,就可以得出两个三元组相交时的深度了,这个方法可以拿40分(居然才给40分QAQ)
我们发现,上面的方法时间主要损耗在往根跑的时候,举个例子(1,2,10000000)这种情况要跳很多次,而且没有意义,为了解决这个问题,我们设t1=y-x,t2=z-y;当t1<t2时显然可以往右边跳(t2-1)/t1(下取整)次,当t1>t2时也是一样的,那么我们就可以一次可以跳很多步了。(注意不能跳到二分深度的上面去了,要判断一下)

贴代码

一堆注释2333

const n=3;
var
    a,b,c:array[0..5]of longint;
    i,j,k,l,r,x,y,z,deep1,deep2,mid:longint;
    bz:boolean;
function min(x,y:longint):longint;
begin
    if x<y then exit(x) else exit(y);
end;
function max(x,y:longint):longint;
begin
    if x>y then exit(x) else exit(y);
end;
procedure work1(p:longint);
var
    i,j,k,l,r:longint;
begin
    l:=y-x;
    r:=z-y;
    if l=r then exit;
    if l<r then
    begin
        k:=(r-1) div l;
        if p-k<mid then k:=p-mid;
        p:=p-k;
        //if k>p then k:=p;
        //p:=p-k;
        x:=x+k*l;
        y:=y+k*l;
        if bz=false then
        deep1:=deep1+k;
    end else
    begin
        k:=(l-1) div r;
        if p-k<mid then k:=p-mid;
        p:=p-k;
        //if k>p then k:=p;
        //p:=p-k;
        y:=y-k*r;
        z:=z-k*r;
        if bz=false then
        deep1:=deep1+k;
    end;
    if p>mid then work1(p);
end;
procedure work2(p:longint);
var
    i,j,k,l,r:longint;
begin
    l:=y-x;
    r:=z-y;
    if l=r then exit;
    if l<r then
    begin
        k:=(r-1) div l;
        if p-k<mid then k:=p-mid;
        p:=p-k;
        //if k>p then k:=p;
        //p:=p-k;
        x:=x+k*l;
        y:=y+k*l;
        if bz=false then
        deep2:=deep2+k;
    end else
    begin
        k:=(l-1) div r;
        if p-k<mid then k:=p-mid;
        p:=p-k;
        //if k>p then k:=p;
        //p:=p-k;
        y:=y-k*r;
        z:=z-k*r;
        if bz=false then
        deep2:=deep2+k;
    end;
    if p>mid then work2(p);
end;
procedure init;
begin
    for i:=1 to n do read(a[i]);
    for i:=1 to n-1 do
        for j:=i+1 to n do
        if a[i]>a[j] then
        begin
            a[0]:=a[i];
            a[i]:=a[j];
            a[j]:=a[0];
        end;
    for i:=1 to n do read(b[i]);
    for i:=1 to n-1 do
        for j:=i+1 to n do
        if b[i]>b[j] then
        begin
            b[0]:=b[i];
            b[i]:=b[j];
            b[j]:=b[0];
        end;
    x:=a[1]; y:=a[2]; z:=a[3];
    work1(maxlongint);
    c[1]:=x; c[2]:=y; c[3]:=z;
    x:=b[1]; y:=b[2]; z:=b[3];
    work2(maxlongint);
    if (c[1]<>x) or (c[2]<>y) or (c[3]<>z) then
    begin
        writeln('NO');
        halt;
    end;
    writeln('YES');
    l:=0;
    //if deep1=0 then deep1:=1;
    //if deep2=0 then deep2:=1;
    //dec(deep1); dec(deep2);
    r:=min(deep1,deep2);
    bz:=true;
end;
begin
    init;
    while l<r do
    begin
        mid:=(l+r) div 2;
        x:=a[1]; y:=a[2]; z:=a[3];
        work1(deep1);
        c[1]:=x; c[2]:=y; c[3]:=z;
        x:=b[1]; y:=b[2]; z:=b[3];
        work2(deep2);
        if (c[1]=x) and (c[2]=y) and (c[3]=z) then l:=mid+1 else r:=mid;
    end;
    //l:=min(deep1,deep2)-l;
    //l:=mid;
    r:=mid;
    mid:=max(l,mid);
    x:=a[1]; y:=a[2]; z:=a[3];
    work1(deep1);
    c[1]:=x; c[2]:=y; c[3]:=z;
    x:=b[1]; y:=b[2]; z:=b[3];
    work2(deep2);
    if (c[1]=x) and (c[2]=y) and (c[3]=z) then l:=mid else l:=mid-1;
    writeln(abs(l-deep1)+abs(l-deep2));
end.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值