BZOJ4692 Beautiful Spacing

84 篇文章 1 订阅
16 篇文章 0 订阅

考虑先二分答案,然后DP

f[i]表示i能否作为一行的结尾

然后我们考虑i作为结尾时,可以作为这一行开头的单词,一个单词j可以作为这一行的单词,当且仅当slen[i]-slen[j-1]+(i-j)*now>=w&&slen[i]-slen[j-1]+i-j<=m;

其中slen为单词长度的前缀和,w为列数,now为当前二分的答案

所以可以作为当前行开头的单词一定是一段区间,我们可以对区间左右端点分别二分求出,假定求出的区间是[l,r],我们只要判断[l-1,r-1]中有没有可以作为一行结尾的单词即可,可以通过对f作前缀和判断

最后找到能作为最后一行的开头的区间[l,n](二分的时候判断条件稍有不同因为不需要最后一列有字母),然后判断[l-1,n-1]中有没有可以作为结尾的即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 50010
#define MAXM 1010
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
#define ll long long
int n,m;
int s[MAXN];
int slen[MAXN];
int len[MAXN];
bool OK1(int l,int r,int mx){
	return slen[r]-slen[l-1]+r-l<=m;
}
bool OK2(int l,int r,int mx){
	return slen[r]-slen[l-1]+(r-l)*mx>=m;
}
bool OK3(int x){
	return slen[n]-slen[x-1]+n-x<=m;
}
bool OK(int x){
	int i;
	memset(s,0,sizeof(s));
	s[0]=1;
	for(i=1;i<=n;i++){
		int l=1,r=i-1;
		int L=i,R=0;
		while(l<=r){
			int mid=l+r>>1;
			if(OK2(mid,i,x)){
				R=mid;
				l=mid+1;
			}else{
				r=mid-1;
			}
		}
		l=1,r=i-1;
		while(l<=r){
			int mid=l+r>>1;
			if(OK1(mid,i,x)){
				L=mid;
				r=mid-1;
			}else{
				l=mid+1;
			}
		}
		s[i]=s[i-1];
		if(L<=R&&(L==1||s[R-1]-s[L-2])){
			s[i]++;
		}
	}
	int l=1,r=n;
	int lim;
	while(l<=r){
		int mid=l+r>>1;
		if(OK3(mid)){
			lim=mid;
			r=mid-1;
		}else{
			l=mid+1;
		}
	}
	return s[n]-s[lim-2]||lim==1;
}
int main(){
	int i;
	while(scanf("%d%d",&m,&n)){
		if(!n&&!m){
			break;
		}
		for(i=1;i<=n;i++){
			scanf("%d",&len[i]);
			slen[i]=slen[i-1]+len[i];
		}
		int l=1,r=m-2;
		int ans;
		while(l<=r){
			int mid=l+r>>1;
			if(OK(mid)){
				ans=mid;
				r=mid-1;
			}else{
				l=mid+1;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

/*
11 4
4 2 1 3
5 7
1 1 1 2 2 1 2
11 7
3 1 3 1 3 3 4
100 3
30 30 39
30 3
2 5 3
0 0
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值