1. 程式人生 > >UVa 714 抄書 二分答案

UVa 714 抄書 二分答案

題意:
把一個包含m個正整數的序列劃分成k個(1≤k≤m≤500)非空的連續子序列,使得每個正 整數恰好屬於一個序列。設第i個序列的各數之和為S(i),你的任務是讓所有S(i)的最大值盡 量小。例如,序列1 2 3 2 5 4劃分成3個序列的最優方案為1 2 3 | 2 5 | 4,其中S(1)、S(2)、S(3) 分別為6、7、4,最大值為7;如果劃分成1 2 | 3 2 | 5 4,則最大值為9,不如剛才的好。每個 整數不超過107。如果有多解,S(1)應儘量小。如果仍然有多解,S(2)應儘量小,依此類推。
分析:
二分最大值,用O(n)掃一遍判斷,T=O(nlogn)。
列印最優解的時候比較麻煩,我WA了幾次,掃後面儘可能取多的數,然後因為要有k個劃分,也就是要k-1個’\’,所以處理到還剩下k個數的時候每隔一個放一個’\’。(注意下標從0開始)

#include<cstdio>
#include<stack>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=555;
int a[N];
int n,k;
bool ok(ll x)
{
    int tot=k-1;
    ll t=0;
    for(int i=0;i<n;i++){
        t+=a[i];
        if
(t>x){ i--; tot--; t=0; if(tot<0)return 0; } } return 1; } int main() { int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&k); for(int i=0;i<n;i++)scanf("%d",&a[i]); if(k==1){ for(int i=0;i<n-1
;i++)printf("%d ",a[i]); printf("%d\n",a[n-1]);continue; } ll l=0,r=5000000000; while(l<r){ ll m=l+(r-l)/2; if(ok(m))r=m; else l=m+1; } stack<int>s; ll t=0; for(int i=n-1;i>=0;i--){ t+=a[i]; if(i+1==k-1)s.push(i),k--; //還剩下i+1個數,最多可以劃分成k個,那麼就是放k-1個'/' else if(t>l){ s.push(i); i++; k--; t=0; } } int i; for(i=0;i<n-1;i++){ printf("%d ",a[i]); if(i==s.top()){ s.pop(); printf("/ "); } if(s.empty())break; } for(i=i+1;i<n-1;i++)printf("%d ",a[i]); printf("%d\n",a[n-1]); } return 0; }