Monthly Expense
Monthly Expense
大意:给你天数N(1 ≤ N ≤ 100,000),和每天需要花的钱(存放在数组中),让你把这些天分成M(1 ≤ M ≤ N)份(每份都是连续的天),要求每份的和最大值尽量小,输出这个和。
思路:二分查找。让数组中的最大值为左界,数组的和为右界。左界的含义是将整个数组分成N块,那么和的最大值就是数组元素中的最大值。右界的含义是将整个数组当做一块,那么最大值就是所有数字之和。那么在左右界中(含左右界)的数肯定有一个是解。
二分搜索:给定一个初始数mid,从数组首元素开始叠加,超出mid那么分组数i加1,那么这么遍历一遍后能得到以mid为解所需的分组数,要是i小于M,说明mid给的大了,反之mid给小了,改变mid之后继续二分。
PS:最开始连题都没看懂,看了别人的题解终于找到一个能看懂的,虽然是二分做的,但是还是不知道二分的本质在什么地方,还要好好研究一下。
1 #include <map> 2 #include <stack> 3 #include <queue> 4 #include <math.h> 5 #include <stdio.h> 6 #include <string.h> 7 #include <iostream> 8 #include <algorithm> 9 #define LL long long 10 using namespace std; 11 #define N 12 13 int n, m; 14 ///通过每次比较group与m的大小,对mid值进行优化 15 bool find_mid(int mid, int a[]) 16 { 17 int sum = 0; 18 int group = 1; ///分组从1遍历到n 19 20 ///从第一天开始向下遍历每天的花费 21 for(int i = 1; i <= n; i++) 22 { 23 if(sum + a[i] <= mid) 24 { 25 ///当前i天之和<=mid时,把他们归并到一组 26 sum += a[i]; 27 } 28 else ///若 前i-1天之和 加上第i天的花费 大于mid 29 { 30 sum = a[i];///则把前i-1天作为一组,第i天作为下一组的第一天 31 group++;///此时分组+1 32 } 33 } 34 if(group > m) 35 return true; ///mid值偏小 36 return false; ///mid值偏大 37 } 38 39 void run() 40 { 41 int a[100010]; 42 while(~scanf("%d%d", &n, &m)) 43 { 44 int right = 0; ///一组划分的时候 上界 45 int left = 0; ///n组划分的时候 下界 46 for(int i = 1; i <= n; i++) 47 { 48 scanf("%d", &a[i]); 49 right += a[i]; ///求上界 50 if(a[i] > left) 51 { 52 left = a[i]; ///求下界 53 } 54 } 55 ///最后的答案肯定就在[left, right]中间 56 int mid = (right+left)/2; 57 while(left < right) ///二分查找 58 { 59 if(find_mid(mid, a)) 60 { 61 left = mid + 1; ///mid值偏小,下界前移 62 } 63 else 64 { 65 right = mid - 1; ///mid值偏大,上界后移 66 } 67 mid = (left+right)/2; 68 } 69 printf("%d\n", mid); ///二分搜索最后得到的mid值必然是使得分组符合要求的最优值 70 } 71 } 72 73 int main(void) 74 { 75 run(); 76 77 return 0; 78 }
注:参考代码:http://blog.csdn.net/lyy289065406/article/details/6648554