数组的子集之和为固定数(大数)

子集和为固定数

题目大概是这么一个意思:

先给出一个正整数 n (1 <= n<=36)和一个正整数sum (1 <= sum <= 2e17),然后给出长度为n的正整数集合a,

对于集合中的每个数ai的范围(1 <= ai <= 2e17),问能否从a中任意挑选m个数,使得这m个数的和等于sum。

若存在(保证唯一性),则输出这m个数,否则输出-1

分析:

很明显这就是一道求子集和等于某某数的例题,所以通常情况下我们会想到使用暴力或者回溯解决。但是我们注意一下这道题sum的范围——最大可以达到2e17,那么常用的暴力法肯定是没办法解决的(时间复杂度为2^n)。而常用的回溯法也因数据过大而无法使用

所以这时候引入一种目前对我而言比较难以想到的算法 → 拆分查找 and 状压dp

其实听起来挺高端的,但实现起来也就是将数组分成两半,对每半数组进行随机组合,在合并判断

下面贴代码:

#include 
using namespace std;
#define ll long long
const int N = 5e2+10;
int n,m,tot;
ll s,a[N];
bool flag;
ll ans[N];  //ans[i]用来表示这个数是否被使用上了 →1表示使用上了 ,0表示未使用 
mapM;
void init()
{
	tot=1;
	memset(ans,0,sizeof(ans));
	flag = false;
	m = n/2;
}
void cf_ztdp()
{
	for (int i = 0; i<1<>j&1) u += a[j+1];
			M[u] = i+1;
		}
		for (int i = 0; i<1<>j&1) u += a[j+m+1];
			int t = M[s-u];
			if (t)
			{
				--t;
				for (int j = 0; j < m; j++)
					ans[tot++] = t>>j&1;
				for (int j = 0; j < n-m; j++)
					ans[tot++] = i>>j&1;
				flag=true;
			}
		}
		tot--;
}
int main()
{
	ios_base::sync_with_stdio(false);
	while(cin>>n>>s)
	{
		init();
		for (int i = 1; i <= n; i++) cin>>a[i];
		cf_ztdp();
		if(flag)
		{
			for(int i=1; i<=tot; i++)
				if(ans[i])  
				i!=tot?cout<

 

        因为我对它的掌握比较差,我就没有添过多的注释,大家要是不太理解代码的实现过程或者根本懒得理解就把它当成模板来使用吧^_^

你可能感兴趣的:(数组的子集之和为固定数(大数))