UVa 714 抄書 二分答案
阿新 • • 發佈:2019-01-24
題意:
把一個包含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;
}