最大值最小化应该是二分法中经典的题目,Copying Books就是一道最大值最小化的题目
题目大致的意思是要抄N本书,编号为1,2,3…N, 每本书有1<=x<=10000000页, 把这些书分配给K个抄写员,要求分配给某个抄写员的那些书的编号必须是连续的。每个抄写员的速度是相同的,求所有书抄完所用的最少时间的分配方案。
设序列的最大值为max,总的和为sum。很显然序列的最大值在[max,sum]之间,找到了上界和下界就可以二分查找。在每次查找的时候,我们可以尽量向右划分,这样可以使划分的最大值尽量最小。如果小于等于查找的数,r = mid,反之 i = mid + 1。找到了最大值中的最小值之后就可以进行划分,划分时只要反向遍历数组,将每个区间尽量向左划即可。不过要注意用 数据过大,要用long long储存,否则会溢出。
题目可以在UVA上提交,不过UVA测评的速度太慢了,可以在https://vjudge.net这个网站上提交。下面是AC的代码。
#include <stdio.h> #include <stdlib.h> #include<iostream> #include<algorithm> #include<memory> #include<limits.h> using namespace std; int n,k,m; int arr[501],ans[501]; int findMid(long long num) { long long sum = 0,count1 = 0; for(int i = 0;i < m;++i) { if(count1 > k - 1) return 0; sum += arr[i]; if(sum > num) { sum = arr[i]; ++count1; } } if(count1 > k - 1) return 0; return 1; } void divid(long long num) { long long sum = 0,count1 = 0; for(int i = m - 1;i >= 0;--i) if(sum + arr[i] > num) { ans[i + 1] = 1; sum = arr[i]; count1++; } else sum += arr[i]; int i = 1; while(i < m && count1 < k - 1) { if(!ans[i]) ans[i] = count1++; ++i; } } int main() { cin >> n; while(n) { memset(arr,0,sizeof(arr)); memset(ans,0,sizeof(ans)); long long sum = 0; int max1 = INT_MIN; cin >> m >> k; for(int i = 0;i < m;++i) { cin >> arr[i]; sum += arr[i]; max1 = max(max1,arr[i]); } long long i = max1,j = sum + 1,mid; while(i < j) { mid = (i + j) / 2; if(findMid(mid)) j = mid; else i = mid + 1; } divid(i); for(int i = 0;i < m - 1;++i) { if(ans[i]) cout << '/' << ' '; cout << arr[i] << ' '; } if(ans[m - 1]) cout << '/' << ' '; cout << arr[m - 1] << endl; --n; } return 0; }
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/20741.html