题目:点击打开链接
题意:给你n,m。有一个数列a,a中有n个元素。输入m+1个数,第i个数表示把数列a当成一个集合,集合a的 和为i的 子集 的个数为b[i],集合a中所有数之和为m。解释样例1:m+1个数为1 1 1 1,表示集合a中和为0的有1个(空集),和为1的有1个({1}),和为2的有1个({2};{1,1}不行,因为{1}只有1个),和为3的有1个({1,2}),且所有数之和为3({1,2})。
思路:从小数向大数推,dp[i]存和为i的子集的个数,如3若能被之前的1,2组合出来,那么dp[3]+=1,则ans中3的个数为b[3]-dp[3]。用背包的思想来求dp[i],dp[i]=dp[k-i],这里的k要从m开始到i,反向的更新i之后的所有数,到i结束,如果到0结束则RE。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+4;
int main()
{
int t,n,m,dp[maxn],b[maxn],a[55],c[maxn];
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);
for(int i=0;i<=m;i++)
scanf("%d",&b[i]);
dp[0]=1;//和为i的子集个数
int cnt=0;
for(int i=1;i<=m;i++)
{
c[i]=b[i]-dp[i];
for(int j=1;j<=c[i];j++)
{
a[cnt++]=i;
for(int k=m;k>=i;k--)
dp[k]+=dp[k-i];
}
}
for(int i=0;i<cnt-1;i++)
printf("%d ",a[i]);
printf("%d\n",a[cnt-1]);
}
return 0;
}