HDU - 6092 Rikka with Subset

时间:2019-02-17
本文章向大家介绍HDU - 6092 Rikka with Subset,主要包括HDU - 6092 Rikka with Subset使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them: 

Yuta has nn positive A1−AnA1−An and their sum is mm. Then for each subset SS of AA, Yuta calculates the sum of SS. 

Now, Yuta has got 2n2n numbers between [0,m][0,m]. For each i∈[0,m]i∈[0,m], he counts the number of iis he got as BiBi. 

Yuta shows Rikka the array BiBi and he wants Rikka to restore A1−AnA1−An. 

It is too difficult for Rikka. Can you help her?  

 

Input

The first line contains a number t(1≤t≤70)t(1≤t≤

Sample Input

2
2 3
1 1 1 1
3 3
1 3 3 1

70), the number of the testcases. 

For each testcase, the first line contains two numbers n,m(1≤n≤50,1≤m≤104)n,m(1≤n≤50,1≤m≤104).

The second line contains m+1m+1 numbers B0−Bm(0≤Bi≤2n)B0−Bm(0≤Bi≤2n).

 

Output

For each testcase, print a single line with nn numbers A1−AnA1−An. 

It is guaranteed that there exists at least one solution. And if there are different solutions, print the lexicographic minimum one.

 

Sample Output

1 2
1 1 1

 

Hint

In the first sample, $A$ is $[1,2]$. $A$ has four subsets $[],[1],[2],[1,2]$ and the sums of each subset are $0,1,2,3$. So $B=[1,1,1,1]$

        

题目大意:一个集合A中有n个元素,集合A中所有的元素和为m,求出集合A所有子集的和,集合B中元素b[i]表示集合A的子集中所有和为i 的个数,给出集合B求出集合A

解题思路:逆向dp,我们设dp[i]表示集合A的子集中和为i的个数,则可以这样求出dp[i]

	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=a[i];j--)
			dp[j]+=dp[j-a[i]];
	}

 根据求dp[i]的方法可以逆向dp来求a[i],我们需要找出集合A的第一个元素从1开始从前往后找,那么第一个b[i]不等于0的i一定是集合A中的最小的那个元素,因为b[i]是集合A的子集中和为i的个数,集合A中最小的那个元素单独一个子集和最小,这时就很容易找到所有集合A的元素,然后逆向dp,每次都把最小的元素加进去,这样最后输出的就是字典序最小的集合A

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e4+10;
typedef long long LL;
LL dp[maxn],b[maxn];
int a[55];
int main()
{
	int t,n,m;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=0;i<=m;i++)
			scanf("%lld",&b[i]);
		for(int i=1;i<=m;i++)
		{
			if(b[i])
			{
				a[1]=i;//找集合A最小的那个元素 
				break;
			}
		}
		int cnt=2;
		for(int i=1;i<=n;i++)////从第一个数反过来DP
		{
			for(int j=a[i],flag=0;j<=m;j++)
			{
				b[j]-=b[j-a[i]];//去掉A的第i和元素时和为j的个数 
				if(b[j]>0&&flag==0)
				{
					a[cnt++]=j;///每次都要把最小的j且b[j]!=0的数加入a中
					flag=1;
				}
			}
		}
		for(int i=1;i<=n;i++)
		{
			if(i==n)
				printf("%d\n",a[i]);
			else
				printf("%d ",a[i]);
		}
	}
	return 0;
}