这是一个我的朋友给我带来挑战的问题。我设法提出了一种适用于小输入的递归算法,但是我得到大值的分段错误。我认为这是因为堆栈溢出。我使用C语言来解决问题。
您将获得一系列n个数字。查找并打印子集的最大长度,使得对于形成该子集的任何两个数字,数字的总和不能被k整除。
输入包含第一行2个数字n和k,在下一行有n个数字a [i],使得:
1 <= n <= 10^5
0 <= a[i] <= 10^9
1 <= k <= 100
# Example input:
4 3
1 7 4 2
# Output:
3
说明:(1 7 4) 1 + 7 = 8; 1 + 4 = 5; 7 + 4 = 11;
所有这些都不能被3整除。
我的解决方案基于以下想法:对于数组中的所有数字,如果它可以被k整除,则检查与其他数字的总和。如果我们找到匹配然后创建2个数组,一个排除和的第一个项,一个排除第二个,这样我们从子集中排除这些对。然后对第一个数组做同样的事情。如果我们检查了数组中的所有元素,那么将解决方案设置为数组的长度并继续应用&#34;求解器&#34;仅限长度大于已找到的解决方案的数组。该算法适用于n #include <stdio.h>
int n, k;
int * deleteElement(int * a, int n, int j){
int *c = (int*) malloc((n-1) * sizeof(int));
int k = 0;
for(int i = 0; i < n; i++){
if(i == j) continue;
c[k] = a[i];
k++;
}
return c;
}
int sol = 0;
void solver(int *a, int n, int *sol){
int *b, *c;
if(n <= *sol) return;
for(int i = 0; i < n-1; i++){
for(int j = i + 1; j < n; j++){
if((a[i] + a[j]) % k == 0){
c = deleteElement(a, n, i);
b = deleteElement(a, n, j);
solver(c, n-1, sol);
solver(b, n-1, sol);
return;
}
}
}
*sol = n;
}
int main(){
scanf("%d", &n);
scanf("%d", &k);
int a[n];
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
solver(a, n, &sol);
printf("%d\n", sol);
return 0;
}
答案 0 :(得分:4)
你可以使用迭代来摆脱你的两个递归调用之一,但这对堆栈空间没有帮助,因为它们具有相同的深度 - 一个调用与2个一样糟糕。
编写一个实际测试所有有效集的完全迭代算法很容易,但这仍然是一个指数时间算法。在任何情况下,这都会使您免于堆栈溢出,运行 way 的时间太长。由于该算法也很糟糕,我不想写它。
解决此问题的合理线性时间方法是:
规则4反映了这样一个事实:对于我们在规则2和3中没有处理的情况,有效子集不能包括任何元素x和y,使得(x + y)%k = 0。最大的有效子集包括MODCOUNTS [i]中的所有元素,或MODCOUNTS [ki]中的所有元素,但不包括两者中的元素。
如果使用稀疏数据结构(如哈希表)来实现MODCOUNTS,则整个过程需要O(N)时间。