Rikka with Subset
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1542 Accepted Submission(s): 781
题目链接:点击打开链接
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 i s 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?
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) .
It is guaranteed that there exists at least one solution. And if there are different solutions, print the lexicographic minimum one.
2 2 3 1 1 1 1 3 3 1 3 3 1
1 2 1 1 1HintIn 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和b,a1---an,b0---bm,a数组里所有数的和为m,b[i]所表示的意思是说a数组的子集和为i的个数为
b[i]个,现在给你n和m,以及b数组里的数,让你按字典树的顺序还原a数组。例如:a数组为[ 1,2 ];那么它的子集
有[ ],[ 1 ],[ 2 ],[ 1,2 ];这些子集的和分别为 0,1,2,3;所以b[ 0 ]=1,b[ 1 ]=1,b[ 2 ]=1,b[ 3 ]=1
分析:
本题是01背包题,先开始不知道,最后自己弄明白之后,感觉确实有点像01背包,因为昨天才开始学的01背包。
对于数组b,除了b[0]外,第一个值不为零的b[i],在a数组里一定存在,且存在的个数就为b[i]个,因为a的子串包括那
些只含有a数组某一个元素的,这些子串的和只有那一个元素的值,往往较小,排在前面,这时候至少可以确定一个a
中的元素,现在要做的是把这已经个确定的元素拿出来,并且对b进行操作,处理成除去 拿出去的那个元素 后,b数
组继续满足原先的定义,就像刚开始那个元素就没有出现过一样。这个时候我们就可以重复上述的步骤,直到取出a
数组中的全部元素。
下面的问题就是怎么对b数组进行处理:
和为j的(总)组合数=和为j的组合数(含有i元素)+和为j的组合数(不含有i元素)我们的目的就是把各项处理成 和
为j的组合数(不含有i元素) 的那种。和为j的(总)组合数为处理前的状态,已知。下面就是确定和为j的组合数
(含有i元素)了,而 和为j的组合数(含有i元素)为 b[j-i] 个,因为有几种值为j的情况 可以由(j-i)+i得到,
那么就有多少种 和为j 的组合数 (含有i元素) ,所以为b[j-i]个。
有b[j]=b[j]-b[j-i];这一个等式 就可以了,这句是本题的核心代码,理解了就好解决。
#include<bits/stdc++.h>
using namespace std;
int b[10050];
int a[600];
int main()
{
int T, n, m, k;
scanf("%d", &T);
while(T--)
{
k = 0;
scanf("%d%d", &n, &m);
for(int i = 0; i<= m; i++)
scanf("%d", &b[i]);
for(int i = 1; i<= m; )
{
if(k >= n)
break;//这时说明已经找到了齐了a数组里的数
if(b[i] != 0)//如果b[i]不等于0,说明a的子集中,有子集和为i的情况
{
a[k++] = i;//因为b数组在不断的更新,所以从每次都可以找到单个符合i情况,也就是a数组里的元素
for(int j = i; j <= m; j++)
b[j] = b[j] - b[j - i];//不断对b数组进行更新操作
}
else
i++;
}
printf("%d", a[0]);
for(int i = 1; i< k; i++)
printf(" %d", a[i]);
printf("\n");
}
return 0;
}