hdu 6092 Rikka with Subset (集合计数,01背包)

Problem Description

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 n positive A1?An and their sum is m. Then for each subset S of A, Yuta calculates the sum of S.

Now, Yuta has got 2n numbers between [0,m]. For each i∈[0,m], he counts the number of is he got as Bi.

Yuta shows Rikka the array Bi and he wants Rikka to restore A1?An.

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

Input

The first line contains a number t(1≤t≤70), the number of the testcases. 
For each testcase, the first line contains two numbers n,m(1≤n≤50,1≤m≤104).
The second line contains m+1 numbers B0?Bm(0≤Bi≤2n).

Output

For each testcase, print a single line with n numbers A1?An.
It is guaranteed that there exists at least one solution. And if there are different solutions, print the lexicographic minimum one.

Sample Input

2

2 3

1 1 1 1

3 3

1 3 3 1

Sample Output

1 2

1 1 1

假设现在有一个元素个数为n的允许有重复元素的集合a,那么这个a的子集就有2^n个子集,现在给你这2^n个子集的每一个的和(cnt[i]表示子集的和为i的子集个数),让你还原a集合

第2个样例意思为子集的和为0 1 2 3 的子集个数分别有1 3 3 1个,原集合a一共有3个元素

思路: 首先我们发现原集合中0的个数是好求的,2^num[0]=cnt[0].那么怎样求剩下的元素呢?

发现如果我们一个一个从小到大求,开一个数组sum[i]表示求到当前位置之前子集和为i的子集个数.

有 num[i]*sum[0]+sum[i]=cnt[i],也就是说一共和为i的子集个数等于集合中数字i的个数乘和为0的子集个数再加上之前那些由>0&&<i的元素构成的子集中和为i的个数

然后我们每次求出一个num[i]后,用完全背包的思想将这num[i]个i一个一个的加入到集合中,用完全背包的思想更新sum数组就行了

代码如下:

 1 #include <bits/stdc++.h>
 2
 3 using namespace std;
 4 typedef long long ll;
 5 const int maxn = 5e5+500;
 6 int n,m;
 7 ll cnt[maxn];
 8 ll sum[maxn];
 9 int num[maxn];
10 int main()
11 {
12     //freopen("de.txt","r",stdin);
13     int T;
14     scanf("%d",&T);
15     while (T--){
16         memset(sum,0,sizeof sum);
17         memset(num,0,sizeof num);
18         scanf("%d%d",&n,&m);
19         for (int i=0;i<=m;++i)
20             scanf("%lld",&cnt[i]);
21         num[0]=0;
22         sum[0]=cnt[0];
23         while((1<<num[0])<cnt[0]) num[0]++;
24         for (int i=1;i<=m;++i){
25             num[i]=(cnt[i]-sum[i])/sum[0];//num[i]*sum[0]+sum[i]=cnt[i]
26             for (int j=1;j<=num[i];++j){//一个一个的加入几个
27                 for (int k=m;k>=i;--k){//完全背包思想更新sum
28                     sum[k]+=sum[k-i];
29                 }
30             }
31         }
32         vector<int> vec;
33         for (int i=0;i<=m;++i){
34             for (int j=0;j<num[i];++j)
35                 vec.push_back(i);
36         }
37         for (int i=0;i<vec.size();++i)
38             printf("%d%c",vec[i],i==(vec.size()-1)?‘\n‘:‘ ‘);
39     }
40     return 0;
41 }
时间: 2024-10-14 10:11:29

hdu 6092 Rikka with Subset (集合计数,01背包)的相关文章

hdu 6092 Rikka with Subset(01背包)

题目链接:hdu 6092 Rikka with Subset 题意: 给你n和m,让你找一个字典序最小的含有n个数的A序列,使得A序列的和为m, 然后给你m+1个数,是A序列所有的集合的和的个数,然后让你找出这个A序列. 题解: 和题解一样的思想. 1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int i=(a);i<=(b);++i) 4 using

HDU 6092 Rikka with Subset 思维 递推

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6092 题目描述: 给你一个集合的所有子集各个和, 让你找到这个集合, 输出字典序最小 解题思路: 下标0上的数字肯定是1, 所以从下标为1的开始向后, 第一个不为零的数的下标肯定是集合中的一个数而且是最小的数......同时将这个数减减, 记住这个下标为cur(所以这个集合应该是唯一的?我没严格证明, 但是每一步取得值肯定都是唯一的啊, 而且从小到大)这样我们向后面偏移cur个单位, 如果两个数都

HDU 3033 I love sneakers! (DP 01背包+完全背包)

Problem Description After months of hard working, Iserlohn finally wins awesome amount of scholarship. As a great zealot of sneakers, he decides to spend all his money on them in a sneaker store. There are several brands of sneakers that Iserlohn wan

USACO Subset 整数划分01背包

又是去理解了一次01背包. 这道题目的意思就是给你一个N (N < 40)表示有一个集合{1,2,3,... n} 你要将它划分成相等的两个子集合,求有几种划分方式 如果N是奇数,那么显然不能由相同的两个Sub Sum组成,所以要输出“0” 现在我们定义一个数组Dp[i][j] 表示前i个数组合起来的和是j的种数 接下来就和01背包很像了 得到状态转移方程Dp[i][j] = Dp[i - 1][j] + Dp[i - 1][j - i] 分表代表当前的i 取 和 不取 在每一层 j 的转移下要

HDU 5887 Herbs Gathering(搜索求01背包)

http://acm.hdu.edu.cn/showproblem.php?pid=5887 题意: 容量很大的01背包. 思路: 因为这道题目背包容量比较大,所以用dp是行不通的.所以得用搜索来做,但是需要一些剪枝,先按体积排序,优先考虑体积大的物品,这样剪枝会剪得多一些(当然按照性价比排序也是可以的). 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdi

HDU 1203 I NEED A OFFER!【01背包】

大意:01背包 分析:01背包 代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 const int maxn = 10005; 7 const int INF = 1000000000; 8 9 double dp[maxn]; 10 int cost[maxn]; 11 double prob[maxn]; 12 int main

hdu 3339 In Action(迪杰斯特拉+01背包)

In Action Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5102    Accepted Submission(s): 1696 Problem Description Since 1945, when the first nuclear bomb was exploded by the Manhattan Project t

HDU 2546 饭卡(带限制的01背包变形)

思路:有几个解法,如下 1)先拿出5块买最贵的菜,剩下的菜再进行01背包.如何证明正确性?设最贵的菜价e,次贵的菜价s,设减去5后的余额为x,会不会产生这样的情况,假设用5元买了e,余额最多能买到x-2钱的菜,那么共买到是x-2+e.而如果挑出s,并且有其他菜价组合加上e等于x呢?不知怎么证明.但是能AC,没有实现. 2)将余额-5作为背包容量,进行01背包,dp时记录下每种背包容量中所不包含的最大菜价,这个菜最后用那5元来买.同样,不知道如何证明. 1 #include <iostream>

hdu 1203 - I NEED A OFFER!(01背包)解题报告

I NEED A OFFER! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 20129    Accepted Submission(s): 8034 Problem Description Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了.要申请国外的任何大学,你都要