将数组划分为k个连续分区,使最大分区之和最小
Divide array into k contiguos partitions such that sum of maximum partition is minimum
这里最大和子集是k个给出最大和的子集之一例如:arr = [10,5,3,7], k = 2k个子集中划分arr的可能方法是
{10,[5,3,7]},{[10,5],[3,7},{[10,5,3],7}
和
{[10,5],[3,7} is the optimal one.
编辑:它相当于https://www.codechef.com/DI15R080/problems/MINMAXTF
假设你知道答案是x,这意味着最大子集的和等于x。你可以通过贪心算法O(n)来验证这个假设。(从左到右遍历数组并选择项目,直到该子集的和小于x)。现在您可以对x进行二进制搜索,并找到x的最小值。算法复杂度 0 (nlogn)
这是一个二进制搜索样本空间的例子。
int min_max_sum(std::vector<int> & a, int K) {
int n = a.size();
long long high = 0, low = 0, mid = 0;
for (int i = 0; i < n; ++i) {
high += a[i];
low = max(a[i], low);
}
while(low <= high) {
mid = (low+high)/2;
long long part_sum = 0;
int parts = 1;
for (int i = 0; i < n; ++i) {
if (part_sum + a[i] > mid) {
part_sum = 0;
parts++;
} else {
part_sum += a[i];
}
}
// if no. of parts in less than (or equal to) K then mid needs to (,or can) be more constrained by reducing upper limit
if (parts <= K) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return mid;
}
复杂度:0 (n log(sum(array))).
但由于对数比线性好得多,这种复杂性是相当好的。
最坏情况的复杂性:O (n日志(INT_MAX * n)) = O (32 n + n log (n)) = O (n log (n))。
让我们从一个例子开始。设N=5, k=3(假设除法后有三部分)。设数组为{1,2,8,4,9}。我们可以观察到,如果k等于1,那么最大分区的和将是sum(所有数组元素)即24,如果k=5,那么最大分区的和将是max(所有数组元素)即9。现在,我们可以观察到,随着k的增加,最大分区的最小值的和减小。我们的算法将在二分搜索的帮助下做到这一点。但是怎么做呢?????通过看这个问题,我们可以看到,我们必须找到最大值中的最小值这引起了像"fffftttttttttt"这样的问题的感觉,我们必须找到第一个t,我们将做完全相同的事情,但将在函数的帮助下完成。(看看代码和答案从这里,并行)…辅助函数将在提供最大分区的最小和时找到K的值。我们将初始化low=max(所有数组元素),这里low=9,high= sum(所有数组元素),即high=24。因此,对于min_max_dist=16,我们的辅助函数将返回所需的k个数。如果number of k> k ,这意味着min_max_dist可以容纳更多的值。因此,我们将把低值增加到mid+1,如果k<=K,这意味着在更少的分区数量下,我们可以实现min_max_dist,但我们可以做更多的分区,因此我们可以将高值减少到mid。因此,我们的代码将以以下方式执行我们的示例:-
low high mid number_of_k
9 24 16 9
9 16 12 2
9 12 10 4
11 12 11 3
,最后返回结果->low=11。
#include <bits/stdc++.h>
#define ll long long int
using namespace std;
ll number_of_k(ll arr[],ll n,ll minimum_max__dist){
ll sum=0;
ll k=1;
for(ll i=0;i<n;i++){
sum+=arr[i];
if(sum > minimum_max__dist){
sum=arr[i];
k++;
}
}
return k;
}
ll Max(ll arr[], ll n)
{
ll max1 = -1;
for (ll i = 0; i < n; i++)
if (arr[i] > max1)
max1 = arr[i];
return max1;
}
ll Sum(ll arr[], ll n)
{
ll sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];
return sum;
}
ll min_max_bin_search(ll arr[],ll n,ll k){
ll low=Max(arr,n);
ll high=Sum(arr,n);
ll mid;
while(low<high){
mid=low+(high-low)/2;
if(number_of_k(arr,n,mid)<=k)
high=mid;
else
low=mid+1;
}
return low;
}
int main()
{
ll n,k;
cin>>n>>k;
ll arr[n];
for(ll i=0;i<n;i++)cin>>arr[i];
cout<<min_max_bin_search(arr,n,k)<<endl;
return 0;
}
该算法的时间复杂度为->O(nlogn)
阅读本文二进制搜索-> https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/和解决这个问题-> http://www.spoj.com/problems/AGGRCOW/
您可以在这里找到关于基于动态规划的解决方案的好文章:https://www.geeksforgeeks.org/painters-partition-problem/和它的复杂度是O(k*N²)
为了得到更好的解决方案,您可以使用其他人建议的二分搜索方法。你可以在这里找到一个更详细的解决方案,使用二分搜索:https://www.geeksforgeeks.org/painters-partition-problem-set-2/,它的复杂度是0 (NlogN)
这可以使用动态规划来解决:
首先定义DP[n,m]
为子数组C[1..n]
划分为m
部分的最优解。其中每个部分至少有一个元素。
DP[n,1] = sum(C1:Cn)
DP[n,n] = max(C1:Cn)
DP[n,m] = min( sum(Ck:Cn) + DP[k-1,m-1] )
where k goes from m to n
解释:DP[n,1]
—基本情况,当分区数为1
时,只有一种方法—所有元素都剩下(从1到n)。DP[n,n]
-当分区的数量等于数组中剩余的元素数量时,只有一种合法的方法来划分它-每个元素在不同的分区中,因此具有最大总和的分区是数组中最大的元素。DP[n,m]
-这是主要的解决方案。我们不知道下一个分区有多少元素,所以我们需要遍历所有选项并从中获得最小值。
除法只是一个蛮力问题。你必须关注最小化的函数。所以你要最小化的是与平均值的偏差。
int sum_arr(int *arr,int size)
{
int res=0;
while (size-->0)
res+=*arr++;
return res;
}
int minimize( const int *array, int size)
{
int i,j,cur_i;
double dev, cur_min,avg=(double)sum_arr(array,size)/size;
for (i=1;i<size-1;i++)
{
dev=abs(avg-sum_arr(array,i));
dev+=abs(avg-sum_arr(array+i,size-i);
if (dev<cur_min)
{
cur_min=dev;
cur_i=i;
}
}
return cur_i;
}
一个简单的代码是:(未测试)
// This code works only if you have to find in a contiguous segment
bool isPossible(vector<int>& nums, int mid, int k) {
int n = nums.size(), Count = 1, Sum = 0;
for(int i = 0; i < n; i++) {
if(nums[i] > mid) return false;
Sum += nums[i];
if(Sum > mid) {
Count++;
Sum = nums[i]
}
}
return Count <= k;
}
int Func(vector<int>& nums, int k) {
int n = nums.size();
int low = 0, high = accumulate(nums.begin(), nums.end(), 0);
while(low <= high) {
int mid = (low + high) / 2;
if(isPossible(nums, mid, k)) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
- Mongodb c++驱动程序:如何查询元素的数组
- 将数组的地址分配给变量并删除
- 从C++本机插件更新Vector3数组
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 数组索引的值没有增加
- 将对象数组的引用传递给函数
- 为char数组调整zlib-zpipe
- 2D数组来自文本输入,中间有空格
- std::向量与传递值的动态数组
- 在c++中用vector填充一个简单的动态数组
- 使用strcpy将char数组的元素复制到另一个数组
- 使用指针从C++中的数组中获取最大值
- C++使用整数的压缩数组初始化对象
- 告诉一个 const char 数组,除了编译时 C 样式的字符串外,它不以 '