NOIPの模拟2016_8_11_t1_钱仓

本文介绍了一种优化的零填充算法,通过寻找最佳起始点而非遍历所有可能位置,显著提高了算法效率。该方法适用于需要将非零元素移动到零元素位置的问题。

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

这里写图片描述
这里写图片描述

题目の大意

拿样例来解释一下:
1
0
0
2
0
0
1
2
2
2
从第六个位置开始,每一次遇到零就找离当前位置最近的一个数移过来就可以了
(4位置的2分别移到位置5和位置6,位置1的1移到位置4,位置10的2移到位置2,3。。。以此类推就可以得到正确的答案)~

比赛时の想法:

先要找到第一个值为0的点,然后开始从后面拉东西来填坑(用一个先进先出的队列就可以了),但是我不知道以哪一个点作为开始点,于是就把每一个0的连续块的第一个都做了一遍,愉快的超时~~~~

正确の做法:

上面方法在找开始位置的时候浪费了很多时间,其实我们可以直接找出那个开始点,而不是每一个可能的开始点都跑一遍
先把输入复制一遍,然后定义k为x+tmp1i=xa[i]tmp 当k>=0时把tmp+1,当k<0时把k的值改为0,并且把tmp的值改为0,意思应该比较明显了,就是当前的串长度为tmp,而在每一个位置都能分配1时还剩余k个点数没有被分配,那么当tmp=n时就说明从当前位置开始做在每一个时刻都不会出现需要的点数比当前拥有的点数多的情况

贴代码

代码还是很简单的3

var
    a,f,ex:array[0..200005]of longint;
    i,j,k,l,n,y,z,tmp:longint;
    bz:boolean;
    min:int64;
procedure work(x:longint);
var
    i,j,head,ed,cc:longint;
    ans:int64;
begin
    bz:=false;
    ans:=0;
    head:=1;
    ed:=1;
    f[1]:=x;
    for i:=x-1 downto x-n+1 do
    begin
        if a[i]>0 then
        begin
            cc:=head;
            for j:=head to ed do
            begin
                if a[i]=0 then break;
                inc(cc);
                dec(a[i]);
                if (j=ed) and (a[i]>1) then
                begin
                    bz:=true;
                    break;
                end;
                ans:=ans+(f[j]-i)*(f[j]-i);
            end;
            head:=cc;
            if bz=true then break;
        end;
        if a[i]=0 then
        begin
            inc(ed);
            f[ed]:=i;
        end;
    end;
    if head<=ed then bz:=true;
    if bz=false then
    if ans<min then min:=ans;
    //a:=ex;
end;
begin
    //assign(input,'1.in'); reset(input);
    readln(n);
    for i:=1 to n do readln(a[i]);
    for i:=1 to n do a[n+i]:=a[i];
    //ex:=a;
    min:=maxlongint*10000;
    {for i:=1 to n do
        if (a[i]=0) and (a[i+1]<>0) then work(i);}
    k:=0;
    tmp:=0;
    for i:=1 to 2*n do
    begin
        k:=k+a[i]-1;
        if k<0 then
        begin
            k:=0;
            tmp:=0;
        end else inc(tmp);
        if tmp=n then
        begin
            work(i);
            break;
        end;
    end;
    writeln(min);
   // close(input);
end.
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值