这道题卡了我们整场,我们想到了二分答案+判断可行性,但是卡在判断可行性上了,首先n^2的复杂度是肯定不行的,正解要优化到O(n)
dp[i]表示以第i个单词作为一行结尾是否可行,初始化dp[0]=1,然后是一个迭代的过程,由dp[i]推出一个区间可行解dp[left~right]=1;接下来从left+1推出下一个区间可行解,最后在加点判断即可。注意要在整个过程中维护一个变量k表示当前最大的满足dp[i]=1的i=k来保证算法是o(n)的。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define mxn 50005
#define LL long long
int a[mxn];
LL s[mxn];
bool v[mxn];
int w, n;
bool check( int d ){
memset( v, 0, sizeof(v) ); v[0] = 1;
int k = 0;
for( int i = 1; i <= n; ++i ){
if(v[n]) return true;
if( v[i-1] == false ) continue;
for( int j = k + 1; j <= n; ++j ){
if( j==i ) continue;
LL sum = s[j] - s[i-1];
if( sum-1 > w ) break;
int tem ;
if((w-sum+j-i+1)%(j-i)!=0)
tem=1;
else tem=0;
tem+=(w-sum+j-i+1)/(j-i);
if( tem <= d ){
k = max( k, j );
v[j] = 1;
}
}
}
for( int i = n; i >= 0; --i ){
int sum = s[n] - s[i-1];
if( sum -1> w ) break;
if( v[i-1] ) return true;
}
if( v[n] ) return true;
return false;
}
int main()
{
while( scanf( "%d%d", &w, &n ) == 2 && w ){
for( int i = 1; i <= n; ++i )
scanf( "%d", a + i ), ++a[i];
s[0] = 0;
for( int i = 1; i <= n; ++i )
s[i] = s[i-1] + a[i];
int l = 1, ans,r = w;
while( l <= r ){
int m = ( l + r ) >> 1;
if( check(m) ){
ans=m;
r = m-1;
}
else l = m + 1;
}
printf( "%d\n", ans );
}
return 0;
}