将数组划分为K个子数组,差异最小

免责声明:

描述的问题看起来像是来自竞赛的任务。我没有参加任何比赛,也没有任何正在进行的比赛,可能涉及到这个问题。如果有任何一个,我将结束问题以保持公平!

我有一个问题:给定值A的数组A和整数K,将A拆分为正好K个不重叠的连续子数组,这样子数组具有最小和的子数组最大和之间的差异就最小。允许将A沿任意方向旋转任意数量。

考虑一个例子:

输入:A = [5 1 1 1 3 2],K = 3

输出:[5] [1 1 1] [3 2],最大和= 5,最小和= 3,结果= 2

我有部分工作代码(非常丑陋,我很糟糕,但这并不代表生产质量):

#include <climits>

#include <cstdio>

#include <cstring>

const int max_n = 50;

const int max_k = 20;

int deps[max_n];

int max (int x, int y) {

return x > y ? x : y;

}

int min (int x, int y) {

return x < y ? x : y;

}

int sum (int a[], int start, int end) {

int res = 0;

for (int i = start; i <= end; ++i) res += a[i];

return res;

}

int k_partitioning(int k, int n, int deps[]) {

int res = INT_MAX;

// consider all possible rotations/shifts

for(int offset = 0; offset < n; ++offset) {

for(int l_min = 0; l_min < n; ++l_min) {

for(int r_min = l_min; r_min < n; ++r_min) {

// check minimal sum subarray

int min_sum = sum (deps, l_min, r_min);

int dp[k][n];

for (int s = 0; s < k; ++s) {

for (int q = 0; q < n; ++q) {

dp[s][q] = 0;

}

}

// assuming that current sum is a target sum

dp[0][r_min-l_min] = min_sum;

for(int p = 1; p < k; ++p) {

for(int l_max = 0; l_max < n; ++l_max) {

for(int r_max = 0; r_max < n; ++r_max) {

int max_sum = sum(deps, l_max, r_max);

if (max_sum >= min_sum) dp[p][r_max] = max(dp[p-1][l_max], max_sum);

} // l_maxs

} // r_maxs

} // partitions

// printing dp

// skip incorrect partitioning, when not all K partitions were used

if (dp[k-1][n-1] == 0) continue;

// update difference

res = min (res, dp[k-1][n-1] - min_sum);

} // end min sum seg

} // start min sum seg

//break;

} // cuts

return res;

}

int main(int argc, char* argv[]) {

int k = 0;

scanf("%d", &k);

int n = 0;

scanf("%d", &n);

for (int i = 0; i < n; ++i) {

scanf("%d", &deps[i]);

}

printf ("%d\n", k_partitioning(k, n, deps));

return 0;

}

这个想法很简单:假设当前分区具有最小总和,枚举所有可能的最大分区,设置动态编程以生成具有最小值的最大总和,检查差异。总复杂度:O(K * N ^ 4)。

我的问题是它无法通过某些测试,因此无法解决问题。有人可以帮我吗?

测试失败,例如:

N = 4,K = 2,A = [6 13 10 2]

更新

此版本应解决以前的一些问题。首先,它消除了“偏移”上的浪费循环,并仅在l_min循环的末尾添加了数组旋转。其次,我注意到,dp不能用0初始化-

这是最小化任务,因此应使用较大的值进行初始化(取决于问题的常量,此处的max_value已经超出了值域)。最后,间隔不应再重叠-

每个和都排除间隔的左端。但是,它仍然无法产生预期的结果。

#include <climits>

#include <cstdio>

#include <cstring>

const int max_value = 200000;

const int max_n = 50;

const int max_k = 20;

int deps[max_n];

int max (int x, int y) {

return x > y ? x : y;

}

int min (int x, int y) {

return x < y ? x : y;

}

int sum (int a[], int start, int end) {

int res = 0;

for (int i = start; i <= end; ++i) res += a[i];

return res;

}

int k_partitioning(int k, int n, int deps[]) {

int res = max_value;

for(int l_min = 0; l_min < n; ++l_min) {

for(int r_min = l_min; r_min < n; ++r_min) {

int min_sum = sum (deps, l_min+1, r_min);

int dp[k][n];

for (int s = 0; s < k; ++s) {

for (int q = 0; q < n; ++q) {

dp[s][q] = max_value;

}

}

// assuming that current sum is a target sum

dp[0][r_min-l_min] = min_sum;

for(int p = 1; p < k; ++p) {

for(int l_max = 0; l_max < n; ++l_max) {

for(int r_max = l_max; r_max < n; ++r_max) {

int max_sum = sum(deps, l_max+1, r_max);

if (max_sum >= min_sum) dp[p][r_max] = max(dp[p-1][l_max], max_sum);

} // l_maxs

} // r_maxs

} // partitions

// skip incorrect partitioning, when not all K partitions were used

if (dp[k-1][n-1] == max_value) continue;

// update difference

res = min (res, dp[k-1][n-1] - min_sum);

} // end min sum seg

// rotate an array to consider different starting points

int tmp[n];

for (int i = 0; i < n; ++i) {

int new_idx = i + n + 1;

tmp[new_idx % n] = deps[i];

}

for(int i = 0; i < n; ++i) deps[i] = tmp[i];

} // start min sum seg

return res;

}

int main(int argc, char* argv[]) {

int k = 0;

scanf("%d", &k);

int n = 0;

scanf("%d", &n);

for (int i = 0; i < n; ++i) {

scanf("%d", &deps[i]);

}

printf ("%d\n", k_partitioning(k, n, deps));

return 0;

}

回答:

好吧,我想我做到了!

想法如下:我们假设最小总和间隔始终从0开始。然后,我们开始从最小间隔的右边界开始枚举最大总和间隔。我们为当前最大间隔构建DP问题,以确定最小最大和。之后,您更新结果并将数组旋转一个。

我的代码在每次迭代中计算电流总和的方式上并不完美。可以预先计算它们,而每次都只对其编制索引。

这段代码可能有一些错误,但是它通过了我的所有测试。

#include <climits>

#include <cstdio>

#include <cstring>

const int max_value = 200000;

const int max_n = 50;

const int max_k = 20;

int deps[max_n];

int max (int x, int y) {

return x > y ? x : y;

}

int min (int x, int y) {

return x < y ? x : y;

}

int sum (int a[], int start, int end) {

int res = 0;

for (int i = start; i <= end; ++i) res += a[i];

return res;

}

int k_partitioning(int k, int n, int deps[]) {

int res = max_value;

for(int offset = 0; offset < n; ++offset) {

int l_min = 0;

for(int r_min = l_min; r_min < n; ++r_min) {

int min_sum = sum (deps, l_min, r_min);

int dp[k][n];

for (int s = 0; s < k; ++s) {

for (int q = 0; q < n; ++q) {

dp[s][q] = max_value;

}

}

// assuming that current sum is a target sum

dp[0][r_min-l_min] = min_sum;

for(int p = 1; p < k; ++p) {

for(int l_max = r_min; l_max < n; ++l_max) {

for(int r_max = l_max; r_max < n; ++r_max) {

int max_sum = sum(deps, l_max+1, r_max);

if (max_sum >= min_sum) {

dp[p][r_max] = min(dp[p][r_max], max(dp[p-1][l_max], max_sum));

}

} // l_maxs

} // r_maxs

} // partitions

// skip incorrect partitioning, when not all K partitions were used

if (dp[k-1][n-1] == max_value) continue;

// update difference

res = min (res, dp[k-1][n-1] - min_sum);

} // end min sum seg

int tmp[n];

for (int i = 0; i < n; ++i) {

int new_idx = i + n - 1;

tmp[new_idx % n] = deps[i];

}

for(int i = 0; i < n; ++i) deps[i] = tmp[i];

} // start min sum seg

return res;

}

int main(int argc, char* argv[]) {

int k = 0;

scanf("%d", &k);

int n = 0;

scanf("%d", &n);

for (int i = 0; i < n; ++i) {

scanf("%d", &deps[i]);

}

printf ("%d\n", k_partitioning(k, n, deps));

return 0;

}

以上是 将数组划分为K个子数组,差异最小 的全部内容, 来源链接: utcz.com/qa/431277.html

回到顶部