问题
给出一组n个正整数,将它们分成k个 子集,然后最小化每个子集的和的平方和.例如,设集合为[1,2,3]和k 为2,则解为[1,2]和[3].第一个子集的和的平方为(1+2)^2=9,第二个子集的和的平方为3^2=9.总和为9+9=18,这是最小的.
样本输入
N=10,k=2 [63230795,3521578,37513838,37860789,30498450,29795141,41263743,5815341,19046274,20919844]->;41895269854617569
N=10,k=5 [42566460,61080136,12375813,29881559,61767889,60645182,22105410,17262225,34309213,38950048] ->;29098109328960071
制约因素
- 1个≤N≤20
- 110
- 这组数字都是正数.您需要使用uint64_t进行算术运算.
我的代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <stdint.h>
bool used[20] = {0};
int n, m;
uint64_t arr[20], min = UINT64_MAX;
int find(int nset, uint64_t sum);
int subset(uint64_t subsum, int cur, int sum, int nset){
if (cur == n){
find(nset+1, sum+subsum*subsum);
return 0;
}
subset(subsum, cur+1, sum, nset);
if (!used[cur]){
used[cur] = 1;
subset(subsum+arr[cur], cur+1, sum, nset);
used[cur] = 0;
}
return 0;
}
int find(int nset, uint64_t sum){
if (sum >= min)
return 0;
if (nset == m-1){
uint64_t setsum = 0;
for (int i = 0; i < n; i++)
if (!used[i])
setsum += arr[i];
sum += setsum*setsum;
if (sum < min)
min = sum;
return 0;
}else{
subset(0, 0, sum, nset);
return 0;
}
}
int main(){
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%llu", &arr[i]);
uint64_t z = 0;
find(0, z);
printf("%llu", min);
}
我的 idea 是使用残酷搜索,当当前解大于当前解,但错误时,用简单的剪枝计算一个子集和下一个子集的平方和.我丢了什么东西吗?感谢您的回复.