1. 程式人生 > >UVA - 714 Copying Book

UVA - 714 Copying Book

題目連結:
UVA - 714 Copying Book
題意:
給定一個m個數的序列,將這個序列分成k份連續子序列,要求子序列的最大值最小。
思路:
二分、貪心
這段序列不用再排序,先求出這段序列的和以及序列的最大值,然後從序列的最大值到序列的和開始二分查詢,找到劃分段數小於等於k的最小上限。
然後根據上面找到的上限,從最後開始往前劃分,因為前面找到的劃分段數可能小於k,這時候把原序列前面部分每個值劃分成一段,使最後的劃分數=k。
注意:
讀入的序列a[505],以及過程中的求和,區間等值記得開long long.

#include <iostream>
#include
<cstdio>
#include<cstring> #include<algorithm> #define ll long long using namespace std; ll a[505],vis[505]; int main(int argc, char** argv) { int Case; scanf("%d",&Case); while(Case--){ int m,k; scanf("%d %d",&m,&k); ll minv=-1; ll total=0;//5*10^9 for(int i=
1;i<=m;i++){ scanf("%lld",&a[i]); if(a[i]>minv) minv=a[i]; total+=a[i]; } ll l=minv,r=total; while(l<r){ ll mid=(l+r)>>1; ll num=1,sum=0,flag=0;//初始化塊的大小很關鍵,num=1 for(int i=m;i>=1;i--){//從後往前劃分,使前面剩下的數儘量小 if(sum+a[i]>mid){ num++; sum=a[i];
if(num>k){ flag=1; l=mid+1; break; } }else{ sum+=a[i]; } } if(!flag) r=mid; } ll sum=0,num=1; memset(vis,0,sizeof(vis)); for(int i=m;i>=1;i--){ if(sum+a[i]>l){ vis[i]=1; sum=a[i]; num++; }else{ sum+=a[i]; } if(k-num==i){//不夠分 for(int j=1;j<=i;j++){ vis[j]=1; } break; } } for(int i=1;i<=m;i++){ if(i==1) printf("%d",a[1]); else printf(" %d",a[i]); if(vis[i]) printf(" /"); } printf("\n"); } return 0; }

思路參考:
http://www.cnblogs.com/g0feng/archive/2012/10/16/2726726.html