这里,最大和子集是给出最大和的k个子集之一 例如:arr = [10,5,3,7]和k = 2 在k子集中划分arr的可能方法是{10,[5,3,7]},{[10,5],[3,7},{[10,5,3],7}和{[10, 5],[3,7}是最佳的。 编辑:它相当于 https://www.codechef.com/DI15R080/problems/MINMAXTF
答案 0 :(得分:5)
假设您知道答案是 x ,这意味着最大子集的总和等于 x 。您可以通过贪婪算法 O(n)来验证此假设。 (从左到右遍历数组并选择项目,直到该子集的总和低于 x )。现在,您可以在 x 上进行二进制搜索,并找到 x 的最小值。该算法的复杂性为 O(nlogn)。
答案 1 :(得分:3)
这是二进制搜索样本空间的示例。
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;
}
复杂性:O(n log(sum(array)))。
但由于logrithms比线性指数好,所以这种复杂性非常好。
最坏情况复杂度:O(n log(INT_MAX * n))= O(32 n + n log(n))= O(n log(n))。
答案 2 :(得分:1)
让我们从一个例子开始。假设N = 5且 k = 3(假设分割后将有三个部分)。让我们的数组为{1,2,8,4,9}。我们可以观察到,如果 k 等于1,那么最大分区的总和将是总和(所有数组元素),即24,如果 k = 5,则总和最大分区将是max(所有数组元素),即9.现在,我们可以观察到随着k的增加,最大分区的最小值之和减小。我们的算法将在二元搜索的帮助下完成。但是怎么做?????通过查看问题 - 我们可以看到,我们必须找到引起问题感觉的最小值,例如“FFFFTTTTTTT”,我们必须找到第一个T.我们将做同样的事情,但将采取这样做的一个函数的帮助。(从这里并行查看代码和答案)..当提供最大分区的最小总和时,辅助函数将找到K的值。我们将初始化low = max(所有数组元素),这里low = 9和high = sum(所有数组元素),即high = 24.因此,mid = 16,所以,对于min_max_dist = 16,我们的辅助函数将返回所需的k数。如果是k&gt; K ,这意味着我们的min_max_dist可以容纳更多的值。所以,我们将低值增加到mid + 1,如果k <= K ,这意味着在较少数量的分区中,我们可以实现min_max_dist,但我们可以做更多的分区,因此我们可以将高值减少到mid.So,我们的代码将在我们的示例中执行以下方式: -
low high mid number_of_k
9 24 16 9
9 16 12 2
9 12 10 4
11 12 11 3
最后我们的结果 - &gt; 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)
阅读关于二元搜索的这篇文章 - &gt; https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/ 和 解决这个问题 - &gt; http://www.spoj.com/problems/AGGRCOW/
答案 3 :(得分:1)
您可以在这里找到有关基于动态编程的解决方案的出色文章:https://www.geeksforgeeks.org/painters-partition-problem/和 它的复杂度为O(k * N ^ 2)。
要获得更好的解决方案,可以使用其他人建议的二进制搜索方法。您可以在此处找到使用二进制搜索的更详细的解决方案:https://www.geeksforgeeks.org/painters-partition-problem-set-2/,其复杂度为O(NlogN)
答案 4 :(得分:0)
这可以使用动态编程解决:
让我们首先将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]
- 这是主要解决方案。我们不确切知道下一个分区有多少元素,所以我们需要检查所有选项并从中获得最小值。
答案 5 :(得分:0)
这个部门只是一个暴力问题。您必须专注于最小化的功能。所以你必须尽量减少与平均值的偏差。
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;
}
一个简单的代码是:(未经测试)