题意是给一个文本排版,求在满足题目所给要求的条件下,最长连续空格最小是多少.
trick: 贪心地模拟是错的,至少无法证明正确性.
正解应该是二分答案+验证.
比较容易想到的验证方法是O(n^2)的.
1 // dp[i] 第i个单词能否为结尾 2 dp[0] = true; 3 for ( i=1 ; i<=n ; i++ ) 4 if (dp[i-1]) 5 { 6 for ( j=i+1 ; j<=n ; j++ ) 7 if (can(i,j)) dp[j] = true; 8 }
但是第二层循环可以优化: 仔细分析可以发现如果当前的 can(i,j) = false , 那么can(i+1,j)也为false;
所以用一个k记录当前最大的dp[]为true的下标,下次从k拓展就行了.
1 llong cnt[maxn]; 2 int n,w,x[maxn]; 3 4 void input() 5 { 6 int i; 7 for ( i=1 ; i<=n ; i++ ) scanf("%d",&x[i]); 8 for ( i=1 ; i<=n ; i++ ) cnt[i] = cnt[i-1]+x[i]; 9 } 10 11 bool dp[maxn]; 12 13 bool check(int d) 14 { 15 int i,j,k; 16 llong sum; 17 memset(dp,0,sizeof(dp)); 18 dp[0] = true; 19 for ( k=0,i=1 ; i<=n ; i++ ) 20 if (dp[i-1]) 21 { 22 for ( j=k ; j<=n ; j++ ) 23 { 24 if (j<=i) continue; 25 sum = cnt[j]-cnt[i-1]+j-i; 26 if (sum>w) break; 27 int space = w - sum; 28 int D = space/(j-i) + (space%(j-i)>0) + 1; 29 if (D<=d) 30 { 31 dp[j] = true; 32 k = j; 33 } 34 } 35 } 36 sum = cnt[n]-cnt[k]+n-k-1; 37 if (sum<=w) return true; 38 else return false; 39 } 40 41 void solv() 42 { 43 int l,r; 44 l = 1; 45 r = (w-1)/2+2; 46 while (l<r) 47 { 48 if (check(mid)) r=mid; 49 else l=mid+1; 50 } 51 printf("%d\n",l); 52 } 53 54 int main() 55 { 56 freopen("test.txt","r",stdin); 57 while (scanf("%d%d",&w,&n)!=EOF) 58 { 59 if (n+w==0)break; 60 input(); 61 solv(); 62 } 63 return 0; 64 }