和为T(枚举每种情况)

问题描述:

从一个大小为n(1<=n<=22)的整数集中选取一些元素,使得它们的和等于给定的值T。每个元素限选一次,不能一个都不选。 集合中任意元素的和都不超过long的范围

Input

第一行一个正整数n,表示整数集内元素的个数。

第二行n个整数,用空格隔开。

第三行一个整数T,表示要达到的和。

Output

输出有若干行,每行输出一组解,即所选取的数字,按照输入中的顺序排列。

若有多组解,优先输出不包含第n个整数的;若都包含或都不包含,优先输出不包含第n-1个整数的,依次类推。

最后一行输出总方案数。

Sample Input 1 

5

-7 -3 -2 5 9

0

Sample Output 1

-3 -2 5

-7 -2 9

2

问题解析:1.将每一种情况列举出来。2.将每一种情况中出现的数字相加。3.输出结果。

1.列举情况:

使用二进制枚举子集的方法将每一种情况表示出来。

二进制枚举子集:

子集:是一个数学概念:如果集合A任意一个元素都是集合B的元素,那么集合A称为集合B子集

二进制:是计算技术中广泛采用的一种数制二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二” 

在二进制的每一位上只存在两种可能,一种是0一种是1,我们把0看做是不选此数字,把1看成是选择此数字。

10011即为选择第一个和第四第五个数字,其余数字不选。

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

我们可以通过这个for循环得到每种情况

接下来就是判断每个位置上的数字情况了(利用按位与运算&)

按位与运算规则:0&0=0;  0&1=0;   1&0=0;    1&1=1;

左移<< :

1<<0=1(1);

1<<1=2(10);

1<<2=4(100);

1<<3=8(1000);

1<<4=16(10000);

...

1<<7=128(10000000);

...

 

我们可以通过将每一种情况对应的二进制数字与    1与不同的数字左移后得到的数字   进行按位与运算

10011  与   1左移零位后得到的数字进行按位与   10011&1 结果为1即第五位被选中 即是下标为0的数组元素被选中

10011  与   1左移一位后得到的数字进行按位与   10011&100 结果为1即第四位被选中  即是下标为1的数组元素被选中

10011  与   1左移二位后得到的数字进行按位与   10011&100 结果为0即第三位未被选中  即是下标为2的数组元素未被选中

10011  与   1左移三位后得到的数字进行按位与   10011&1000 结果为0即第二位未被选中  即是下标为3的数组元素未被选中

10011  与   1左移四位后得到的数字进行按位与   10011&10000 结果为1即第一位被选中  即是下标为4的数组元素被选中

将被选中的数字进行相加,看是否等于输入要求的数字,如果等于即输出结果,并将结果数加一。

将每一种情况进行此运算即可得到全部的结果。

AC代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n,a[100],m,num,result = 0;
	cin >> n;
	for(int i = 0; i<n; i++) {
		cin>>a[i];
	}
	cin>>m;
	for(int i = 0; i < (1<<n); i++) { //从0~2^n-1个状态
		num = 0;
		for(int j = 0; j < n; j++) { //遍历二进制的每一位
			if(i & (1 << j)) { //判断二进制第j位是否存在
				num += a[j];//如果存在输出第j个元素
			}

		}
		if(num==m&&i!=0) {//如果等于结果便将结果输出并将result加一 
			for(int j = 0; j < n; j++) { 
				if(i & (1 << j)) { 
					cout<<a[j]<<" ";
				}
			}
			result++;
			cout<<endl;
		}
	}
	cout<<result<<endl;
	return 0;
}

 

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值