ICPC Pacific Northwest Regional Contest 2016 - Barbells(状态压缩+枚举子集的子集)

Your local gym has n barbells and m plates. In order to prepare a weight for lifting, you mustchoose a single barbell, which has two sides. You then load each side with a (possibly empty) setof plates. For safety reasons, the plates on each side must sum to the same weight. What weightsare available for lifting? 

For example, suppose that there are two barbells weighing 100 and 110 grams, and five platesweighting 1, 4, 5, 5, and 6 grams, respectively. Then, there are six possible weights available forlifting. The table below shows one way to attain the different weights:

Barbell Left side Right side Total
100 0 0 100
100 5 5 110
100 1 + 5 6 112
110 5 5 120
110 1 + 5 6 122
110 5 + 5 4 + 6 130

Input

The first line of input contains the space-separated integers n and m (1 ≤ n, m ≤ 14).The second line of input contains n space-separated integers b1, ..., bn (1 ≤ bi ≤ 108), denoting theweights of the barbells in grams.The third line of input contains m space-separated integers p1, ..., pm (1 ≤ pi ≤ 108), denoting theweights of the plates in grams.

Output

Print a sorted list of all possible distinct weights in grams, one per line.

样例输入复制

2 5
100 110
5 5 1 4 6

样例输出复制

100
110
112
120
122
130

题目链接:点击查看

题目大意:给出 n 个杠铃的杆子,和 n 个配重块,满足条件的杠铃搭配是:一个杠铃的杆子 + 左右重量相等的配重块,现在问可以组合出多少不同重量的杠铃

题目分析:因为对杆子没有什么限制,倒是对配重块有限制,因为 m 比较小,所以可以选择对配重块的选择方案进行状态压缩,那么该如何判断当前选择的配重块能否恰好分成重量相等的两堆呢?一开始我读题以为每个配重块最多只有 108 ,所以想都没想直接上了 01 背包,果不其然的 RE 了两发再仔细读了读样例,发现他说的是 10^8 ,这样的话我们就可以对于每个状态,枚举子集,也就是枚举子集的子集,子集的意思是:选择这些位置为 1 的配重块,子集的子集的意思是:选择这些位置为 1 的配重块分为一堆,这样我们只需要判断一下剩下的那些位置为 1 的配重块的重量是否和当前的这堆相等即可

枚举子集的子集时间复杂度只有 3^n ,然后为了降低时间复杂度,可以预处理一下 sum 数组,sum[ i ] 代表状态 i 中所有位置为 1 所代表的配重块的重量之和

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;
   
typedef long long LL;
   
typedef unsigned long long ull;
   
const int inf=0x3f3f3f3f;
   
const int N=15;

int a[N],b[N],n,m;

int sum[(1<<N)+100];

set<int>st,ans;

void solve(int s)
{
	for(int i=s;i;i=(i-1)&s)
	{
		if(sum[i]==sum[i^s])
		{
			st.insert(sum[i]*2);
			break;
		}
	}
}

void init()
{
	for(int i=0;i<1<<m;i++)
	{
		sum[i]=0;
		for(int j=0;j<m;j++)
			if((i>>j)&1)
				sum[i]+=b[j];
	}
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//  freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
		scanf("%d",a+i);
	for(int i=0;i<m;i++)
		scanf("%d",b+i);
	init();
	for(int i=0;i<1<<m;i++)
		solve(i);
	st.insert(0);
	for(int i=0;i<n;i++)
		for(auto v:st)
			ans.insert(a[i]+v);
	for(auto v:ans)
		printf("%d\n",v);







    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/106890049