题目描述:
小易邀请你玩一个数字游戏,小易给你一系列的整数。你们俩使用这些整数玩游戏。每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字。 例如: 如果{2,1,2,7}是你有的一系列数,小易说的数字是11.你可以得到方案2+2+7 = 11.如果顽皮的小易想坑你,他说的数字是6,那么你没有办法拼凑出和为6 现在小易给你n个数,让你找出无法从n个数中选取部分求和的数字中的最小数(从1开始)。
思路:
(1) 如果对数论有了解,可以用简便的解法:https://www.nowcoder.com/questionTerminal/876e3c5fcfa5469f8376370d5de87c06
(2) 对数论不了解,可以利用全排列来做。每个数字都有 {被使用,不使用} 两种状态,分别用 1 和 0 表示,恰恰对应计算机底层数字的二进制表示,如:给定数组 int a[3] = {1, 2, 3}
0 0 0 : 表示不使用任何数
0 0 1:表示使用第三个数,即3
0 1 0:表示使用第二个数,即2
0 1 1:表示使用第二个和第三个数,即 2 和 3
......
1 1 1:表示使用第1,2,3个数
因此,通过遍历 i = 1 到 2^n (n 为数组长度)就可以穷举所有的可能性。代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
#include<string>
#include<set>
#include<queue>
using namespace std;
inline long long max(long long a, long long b){
return (a > b) ? a : b;
}
inline long long min(long long a, long long b){
return (a > b) ? b : a;
}
int n, a[22];
int maxn = 1000000000;
bool visited[2000002] = { false };
int main(int argc, char * argv[])
{
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
set<int> unique_nums;
/**************** 计算机底层二进制编码 *****************/
// 利用计算机内部天然的0/1二进制编码,得出 2^n 的全部方案:
// Cn(1) + Cn(2) + Cn(3) ... + Cn(n)
for (int num = 1; num <= (int)pow(2.0, 1.0*n); num++){
int sum = 0;
// 依次检查 num 的第1个bit,第二个bit,..., 第n个bit
for (int i = 1; i <= n; i++){
int bit_pattern = 1 << (i - 1);
int one_or_zero = num & bit_pattern;
if (one_or_zero != 0)
sum += a[i];
visited[sum] = true;
}
}
// 找到最小的
for (int i = 1; i <= 2000000;i++)
if (! visited[i] )
{
cout << i << endl;
break;
}
return 0;
}