[二分][DP]JZOJ 3242 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

分析

比赛的时候一看:宝崽题,然后快乐地打起了贪心……

贪心能拿70pt,血赚

正解应是从最大值最小处琢磨,可以二分加DP验证

那么先做个前缀和,方便求单词总长度

我们设f[i]表示第i个单词作为某行结尾的可能性,能为1,不能为0

显然f[0]初始化为1,其余为0

那么考虑一个可转移的j,必定满足f[j]==1

而且,s[i]-s[j]单词总长度+i-j-1用最小空格填充缝隙所需长度<=w宽度

同时满足s[i]-s[j]+mid*(i-j-1)>=w,因为你用最大空格填充,一定要能够填满

然后由于第n个单词不一定在行末,只需要满足第一个要求即可,需要特殊处理

 

#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;
typedef long long ll;
const int N=5e4+10;
int w,n,ans;
bool f[N];
ll s[N];

bool Calc(int x) {
    int j=0,k=j-1;
    memset(f,0,sizeof f);
    f[0]=1;
    for (int i=1;i<n;i++) {
        for (;j<i&&s[i]-s[j]+x*(i-j-1)>=w;j++) if (f[j]) k=j;
        f[i]=k!=-1&&s[i]-s[k]+i-k-1<=w;
    }
    for (int i=n-1;i>=0;i--) if (f[i]&&s[n]-s[i]+n-i-1<=w) return 1;
    return 0;
}

int main() {
    scanf("%d%d",&w,&n);
    for (int i=1,a;i<=n;i++) scanf("%d",&a),s[i]=s[i-1]+a;
    int l=1,r=w-2;
    while (l<=r) {
        int mid=l+r>>1;
        if (Calc(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
}
View Code

 

posted @ 2019-07-02 07:36  Vagari  阅读(202)  评论(0编辑  收藏  举报