C语言 数组部分和问题

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;
}

本人水平有限,麻烦各位大佬指点一下文章中需要改进的地方,谢谢。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值