zoukankan      html  css  js  c++  java
  • jzoj3242. Spacing

    Description

    排版是很有讲究的。假设稿纸的宽度是W个字符,长度不限,当你对一篇文章排版时,必须满足以下条件:
    1.必须保持单词的次序。下图显示了对4个单词“This is a pen”在一张宽11字符的稿纸上排版的结果:
    在这里插入图片描述

    Input
    输入的第一行是用空格分隔的两个正整数W和N(3<=W<=80000,2<=N<=50000),分别代表稿纸的宽度和单词数。接下来有N个正整数,第i个正整数xi代表第i个单词的长度(1<=xi<=(W-1)/2)。

    Output

    输出一个整数,代表最美观的排版中,最多出现多少个连续空格。

    Sample Input

    输入1:
    11 4
    4 2 1 3

    输入2:
    5 7
    1 1 1 2 2 1 2

    输入3:
    11 7
    3 1 3 1 3 3 4

    输入4:
    100 3
    30 30 39

    输入5:
    30 3
    2 5 3

    Sample Output

    输出1:
    2

    输出2:
    1

    输出3:
    2

    输出4:
    40

    输出5:
    1

    Data Constraint

    2<=N<=50000

    题解

    这题十分的妙♂。
    为什么呢?我们看到数据范围,发现没有部分分。
    所以无脑乱打是不能准确估分的。
    然鹅其实数据给水了,n2n^2做法98分。血赚不亏。

    98%
    我们考虑DP。
    f[i]f_{[i]}表示当且以第ii个字符为某行的结尾的最小最大空格。
    转移我们再枚举一个f[j]f_{[j]}来转移,利用前缀和来辅助转移。
    这样就是n2n^2的了,比较简单。

    100%
    我打死也不会告诉你把j的取值范围手动调小一点就能过
    正解是可以把n调成200000的。
    (不知道出题人的brain是怎么了)
    我们看到最小最大,想到什么?二分of course。
    那么我们考虑二分一个答案,然后判断答案是否合法。
    状态还是f[i]f_{[i]}但是其中记录的可以改成true或false表示当前以ii为某行结尾,是否满足答案。
    然后,我们发现,由于我们枚举了答案,我们jj的枚举范围就可以得到了。
    两个东东:
    sumisumj+ij1&lt;=Wsum_i-sum_j+i-j-1&lt;=W
    sumisumj+mid(ij1)&gt;=Wsum_i-sum_j+mid*(i-j-1)&gt;=W
    依照这两个玩意依次推进即可。
    很像单调队列是吧~
    随意转移。

    标程

    98%

    uses math;
    var
            i,j,n:longint;
            m,ans,zd,l,k:int64;
            f,a,sum:array[0..50000] of int64;
    begin
            //assign(input,'spacing.in');reset(input);
            readln(m,n);
            for i:=1 to n do
            begin
                    read(a[i]);
                    sum[i]:=sum[i-1]+a[i];
            end;
            fillchar(f,sizeof(f),127 div 3);
            f[0]:=0;
            zd:=f[1];
            for i:=2 to n-1 do
            begin
                    for j:=i-2 downto 0 do
                    begin
                            if j=1 then continue;
                            if (sum[i]-sum[j])+(i-j-1)>m then break;
                            k:=m-(sum[i]-sum[j]);
                            if k mod (i-j-1)>0 then l:=k div (i-j-1)+1
                            else l:=k div (i-j-1);
                            if (f[j]<>zd) and (f[j]>l) then l:=f[j];
                            if f[j]<>zd then
                            f[i]:=min(f[i],l);
                    end;
            end;
            ans:=1000*maxlongint;
            for i:=n-1 downto 0 do
            begin
                    if (sum[n]-sum[i])+(n-i-1)>m then break;
                    ans:=min(ans,f[i]);
            end;
            if ans=0 then ans:=1;
            writeln(ans);
    end.
    

    100%

    uses math;
    var
            i,j,n,r:longint;
            m,ans,zd,l,k,st,en,mid,answer:int64;
            a,sum:array[0..50000] of int64;
            f:array[0..50000] of boolean;
    function pd(mid:longint):boolean;
    var
            i,j,k,r,rr:longint;
    begin
            fillchar(f,sizeof(f),false);
            f[0]:=true;
            r:=0;
            rr:=-maxlongint;
            for i:=2 to n-1 do
            begin
                    while (r<i) and (sum[i]-sum[r]+mid*(i-r-1)>=m) do
                    begin
                            if f[r]=true then
                            begin
                                    rr:=r;
                            end;
                            inc(r);
                    end;
                    if (rr<>-maxlongint) and (sum[i]-sum[rr]+(i-rr-1)<=m) then
                    begin
                            f[i]:=true;
                    end
                    else f[i]:=false;    {
                    while (sum[i]-sum[rr]+(i-rr-1) div 2)>=m do inc(rr);
                    while (sum[i]-sum[r])+(i-r-1)<=m do inc(r);
                    for j:=rr downto r do
                    begin
                            if (sum[i]-sum[j])+(i-j-1)>m then break;
                            if f[j]<>zd then
                            begin
                                    k:=m-(sum[i]-sum[j]);
                                    if k mod (i-j-1)>0 then l:=k div (i-j-1)+1
                                    else l:=k div (i-j-1);
                                    if (f[j]<>zd) and (f[j]>l) then l:=f[j];
                                    if f[j]<>zd then
                                    f[i]:=min(f[i],l);
    
                            end;
                    end;        }
            end;
            ans:=1000*maxlongint;
            for i:=n-1 downto 0 do
            begin
                    if (sum[n]-sum[i])+(n-i-1)<=m then
                    begin
                            if f[i]=true then exit(true);
                    end;
            end;
            exit(false);
    end;
    begin
            //assign(input,'spacing.in');reset(input);
            readln(m,n);
            for i:=1 to n do
            begin
                    read(a[i]);
                    sum[i]:=sum[i-1]+a[i];
            end;
            st:=1;
            en:=m;
            answer:=1;
            while st<=en do
            begin
                    mid:=(st+en) div 2;
                    if pd(mid) then
                    begin
                            en:=mid-1;
                            answer:=mid;
                    end
                    else
                    begin
                            st:=mid+1;
                    end;
            end;
            writeln(answer);
    end.
    end.
    
    
    
  • 相关阅读:
    Android应用插件式开发解决方法
    给windows服务打包,并生成安装程序
    如何在VS2013中新建WindowsService定时任务
    从源代码分析Android-Universal-Image-Loader图片下载技巧
    Android内存溢出解决方案(OOM)
    Android网络传输中必用的两个加密算法:MD5 和 RSA (附java完成测试代码)
    Volley的基本用法
    Redis 5种数据类型,2种特殊数据处理策略
    SiteWhere物联网云平台架构
    杭电1276 士兵队列训练问题
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11148352.html
Copyright © 2011-2022 走看看