Uva714 Copying Books 【二分】【例題8-10】
阿新 • • 發佈:2019-02-14
題意:將m個序列分成k個連續的子序列,將各個子序列求和,分的子序列的和的最大值儘量小。
思路:
(1)列舉子序列和的最大值,從m個序列中最大值~m個序列的和 之間的數二分列舉最大值,每次列舉的值看能劃分的個數與k值比較,如果<=k 列舉值小縮小,否則增大,直到二分結束後,左值即為最大值的儘量小化。如果直接找到一次相等k的話不能不是儘量小化,所以繼續進行二分!
(2)求劃分個數:遍歷一遍序列,連續累加數字,當加上下一位時大於當前列舉的最大值時就要劃分了!
(3)列印劃分:用一個標記陣列,倒的遍歷序列(因為要靠前的儘量小,所以先算後面的儘量達到最大值,剩下前面的數肯定少了就小了!)繼續連續累加數字,當加上下一位大於最大值的話就將此位置標記1即為劃分,並且將劃分次數減少一次, 或者 當劃分次數大於等於剩餘數個數時必須劃分了,因為有可能連續和還沒有加到最大值,還一直加,此時剩餘的個數不夠滿足k次劃分了!
(4)輸出原序列且當標記陣列為1時輸出"/"。
參考:紫書程式碼庫
程式碼:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; const int maxn = 500 + 5; int s[maxn],mark[maxn],n,k; inline int P(LL v){//最大值最小化為v時劃分的次數 int cnt = 1; LL sum = 0; for(int i=0;i<n;i++){ if(sum + s[i] < v) sum += s[i];//向右劃分 else{sum = s[i];cnt++;}//記錄劃分次數 } return cnt; } inline int binarySearch(LL l,LL r){//二分查詢最大值 while(l < r){ LL pos = l + (r - l)/2; if(P(pos) <= k) r = pos;//當劃分次數小於k時說明當前最大值大了,右值縮小 else l = pos + 1;// } return l; } inline void divide(int ans){//劃分 memset(mark,0,sizeof(mark)); LL sum = 0; int cnt = k; for(int i=n-1;i>=0;i--) if(sum + s[i] > ans || i+1 < cnt){//當和大於最大值 或 剩餘的元素個數已經等於當前剩餘的劃分個數時必須劃分,因為要求劃分k次! sum = s[i];//將求和歸位,繼續下一次的相加 mark[i] = 1;//在當前位置劃分 cnt--;//劃分次數減少 } else sum += s[i]; for(int i=0;i<n-1;i++){ printf("%d ",s[i]); if(mark[i]) printf("/ "); } printf("%d\n",s[n-1]); } int main() { int t; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&k); int maxp = -1; LL sum = 0; for(int i=0;i<n;i++) { scanf("%d",&s[i]); maxp = max(maxp,s[i]);//求最大值,用作二分查詢最大值的左值 sum += s[i];//求和,用作二分查詢最大值的右值 } divide(binarySearch((LL)maxp,sum)); } return 0; }