牛牛有一个n个数字的序列
,现在牛牛想把这个序列分成k段连续段,牛牛想知道分出来的k个连续段的段内数字和的最小值最大可以是多少?
4,2,[1,2,1,5]
4
有3种分法[1],[2,1,5],数字和分别为1,8,最小值为1[1,2][1,5],数字和分别为3,6,最小值为3[1,2,1],[5]数字和分别为4,5,最小值为4则最小值的最大值为4
第一个参数整数n代表序列数字个数
第二个参数整数k代表分出的段数
第三个参数a 包含n个元素代表n个数字
# # 分组 # @param n int整型 (4640)# @param k int整型 # @param a int整型一维数组 (4641)# @return int整型 # class Solution: def solve(self, n, k, a): x, y = 0, sum(a) if k == 1: return y while y - x > 1: mid = (x + y) / 2 segment = 0 nowval = 0 for i in range(len(a)): nowval += a[i] if nowval >= mid: segment += 1 nowval = 0 if segment >= k: x = mid else: y = mid return round((x + y) / 2)
一下想不出没关系的,我们再来问自己几个问题:
连续段段内数字之和最小是多少?
连续段段内数字之和最大是多少?
其实,我们已经问出了一个新的遍历条件,那就是这道题答案的可能范围。
也就是说我们知道了答案的可能值最小为数组元素的最小值,最大为数组元素之和,最终的答案就在由数组元素最小值与数组元素之和框定的范围之中。到这里,我们的思路就可以转换了:从正面罗列所有可能的切分然后逐一比较,到罗列所有可能的答案再逐一排除。
我们先假设这道题的答案是answer,然后再来问自己一个问题,这个问题也就是真正的答案需要满足的条件:
意味着什么?
这个答案好像是显而易见的,我们再问深一点,挖掘挖掘潜在的约束条件:
连续段段内数字之和最小,意味着什么?
好了,现在我们来细致地分析下这两个约束条件条件。其他连续段段内数字之和都比大,那么就有
。不等式转换下就有
,也就是说如果我们按照和大于等于
作为切割条件,最终可以获得的连续段个数至少为
。
综上分析,我们得到了一个可行的解决方法:依次尝试范围内的每一个值,并以相应值为切割条件尝试对数组
进行切分,如果不能切成至少
段,那么就再继续遍历。
#coding=utf-8 import sys class Solution: def solve(self , n , k , a ): # write code here margin_min = min(a) margin_max = sum(a) # margin_max = int(sum(a)/k) for p_v in range(margin_max,margin_min-1,-1): tmp_sum = 0 segment_count = 0 for item in a: tmp_sum+=item if tmp_sum>=p_v: segment_count+=1 tmp_sum=0 if segment_count>=k: return p_v
除二分法外,公众号内还有关于这道题的回溯及动态规划解法的详细解析以及代码实现相关文章。
class Solution { public: /** * 分组 * @param n int整型 * @param k int整型 * @param a int整型vector * @return int整型 */ int solve(int n, int k, vector<int>& a) { int l=0, r = 0, m, cnt, t; for(int i=0;i<n;i++) r += a[i]; if(k==1) return r; while(l+1<r){ m = (l+r)>>1; cnt = t = 0; for(int i=0;i<n;i++){ t += a[i]; if(t>=m){ cnt++; t = 0; } } if(cnt>=k) l = m; else r = m; } return (l+r)>>1; } };
import java.util.*; public class Solution { public int solve (int n, int k, int[] a) { if(a.length == 0){ return 0; } int sum = 0; for(int i = 0; i < n; i ++){ sum = sum + a[i]; } if(k == 1){ return sum; } int low = 0; int high = sum/k; while(low <= high){ int mid = low + (high - low) / 2; if(check(a, k, mid)){ low = mid + 1; }else{ high = mid - 1; } } return (low+high)/2; } public boolean check(int a[], int k, int min_max_sum){ int count = 0; int sum = 0; for(int j = 0; j < a.length; j++){ sum += a[j]; if(sum >= min_max_sum){ sum = 0; count += 1; if(count >= k){ return true; } } } return false; } }
for i in a: sum_+=i if sum_>=mean_: seg+=1 sum_=0
垃圾的我在刷二分 6-29 ```cpp class Solution { public: /** * 分组 * @param n int整型 * @param k int整型 * @param a int整型vector * @return int整型 */ bool check(vector<int>& a, int v, int tar) { int k = 1; int sum = 0; for(int i = a.size() - 1; i >= 0; i--) { sum += a[i]; if(sum > v) { k++; sum = 0; } } return k <= tar; //分的包的mid容量大, 包的数量k 小于等于目标值tar 是合法的 } int solve(int n, int k, vector<int>& a) { int left = 0; int right = 0; for(int i = 0; i < a.size(); i++) { left = min(a[i],left); right += a[i]; } while(left < right) { int mid = (left + right) / 2; if(check(a,mid,k)) { right = mid; } else { left = mid + 1; } } return left; } };
class Solution { public: /** * 分组 * @param n int整型 * @param k int整型 * @param a int整型vector * @return int整型 */ int solve(int n, int k, vector<int>& a) { int low = 0, high = 0; for(auto i : a){ high += i; } while(high - low > 1){ int mid = low + ((high - low) >> 1); int sum = 0; int nGroup = 0; for(auto i : a){ sum += i; if(sum >= mid){ nGroup += 1; sum = 0; } } if(nGroup >= k) low = mid; else high = mid; } int sum = 0; int nGroup = 0; for(auto i : a){ sum += i; if(sum >= high){ nGroup += 1; sum = 0; } } return (nGroup >= k)? high : low; } };