1. 问题: 数组部分和问题
有一个数组 a[N] (N <= 20), 从数组a中任选M个元素(M <= N), 使得其和为K。
M , K由用户自己输入。
2. 解题方法:穷举法
列举出该数组每个子数组,并且求出每个子数组的和,判断该子数组是否满足题意。
2.1难点在于如何列举出每一种情况:
假设有一个长度为8的一维数组:
a[N] = {2, 4, -3, 5, -4, 8, -2,-6}; // N = 7
2.2我们用二进制码来标记这个数组,
假设原数组中的某一个数字存在于子数组中为1,不存在为0,
例:
(a[N]中, N == 7)
(原数组中, 从右至左,依次为第0,1,2,3 … 位 )
(二进制码中,从右至左,依次为第0,1,2,3 … 位 )
则从0000 0001到1111 1111的所有序列中,包含了原数组所有可能出现的子数组的情况
2.3将我们定义的标记与原数组联系起来:
((i>>j)&1)为真,则a[ j ]存在于以 i 为标记的子数列中
例1:
标记: 1 (10进制)----> 0000 0001(二进制 )
判断: ((1>>0)&1)为真
结果:a[0] 存在于以 1 为标记的子数列中,并构成了这个子数列
例2
标记:13(10进制)----> 0000 1101(二进制 )
判断:((13>>0)&1)、((13>>2)&1)和((13>>3)&1)都为真,
结果:a[0]、a[2] 和 a[3] 都存在于以 13为标记的子数列中,并构成了这个子数列
3. 主要程序框架
for(i = 1; i < (1 << (N+1) ); i++)
{
//遍历得到所有子数组,
for(j = 0; j <= N; j++ )
{
//求取该子数组的和
}
if(count == M && sum == K ) //子数组中元素个数为M,子数组元素和为K
{
//该子数组满足条件,打印结果
}
}
4. 最终程序
#include <stdio.h>
#define N 7
int main()
{
int a[N] = {2, 4, -3, 5, -4, 8, -2,-6};
int M, K; //任选M个元素,使得其和为K
int i, j;
int flag = 1; //是否存在这个子数组
//flag为0时存在这个子数组,为1时不存在这个子数组
int sum, count; //sum 用来与K比较,sum == K时满足题意
//count用来与M比较,count == M时满足题意
scanf("%d,%d", &M, &K);
printf("从数组a中选取%d个元素,使得其和为%d\n", M, K);
for(i = 1; i < (1 << (N+1)); i++)
{
//i控制的循环可以遍历a[N]中的每一个子数组
sum = 0; //sum 用来与K比较,sum == K时满足题意
count = 0; //count用来与M比较,count == M时满足题意
for(j = 0; j <= N; j++ )
{
//求取该子数组的和
if((i>>j)&1) //((i>>j)&1)为真,则a[j]存在于以i为标记的子数列中
{
sum += a[j]; //求子数组的和
count ++; //计算子数组元素的个数
}
}
if(count == M && sum == K)
{
//该子数组满足任选M个元素,使得其和为K,打印结果
count = 0;
printf("%d =", K);
for(j = 0; j < N; j++ )
{
if((i>>j)&1)
{
if(count == M-1)
{
printf("%d\n", a[j]); //count == K时输出最后一个数
flag = 0; //存在这个数组,令flag == 0
break;
}
printf(" %d +", a[j]);
count ++;
}
}
}
}
if(flag) //不存在这个子数组,flag == 1
{
printf("不存在这个子数组\n");
printf("使得从a中任选%d个元素,使得其和为%d\n", M, K);
}
return 0;
}
本人水平有限,麻烦各位大佬指点一下文章中需要改进的地方,谢谢。