问题描述:
从一个大小为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;
}