hdu 6092(一)



Rikka with Subset

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 139    Accepted Submission(s): 49


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  A1An  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  A1An .

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

Input
The first line contains a number  t(1t70) , the number of the testcases. 

For each testcase, the first line contains two numbers  n,m(1n50,1m104) .

The second line contains  m+1  numbers  B0Bm(0Bi2n) .
 

Output
For each testcase, print a single line with  n  numbers  A1An .

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
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]$
 

Source

题意:有一个数列 a[] ,长度(n<=50)。b[i] 表示元素和为 i 的集合个数。给你一个数列 b[] ,长度(m<=10000),让你求 a[],并按照其字典序最小输出


显然数字0的数量num[0]为log2(b[0]),数字1的数量num[1]为b[1]/b[0]

设dp[i],表示在当前i没有的情况下,用前面已知数量的数组成数字i共有多少种情况

那么b[i]-dp[i]即为数字i与0进行组合的可能性,则num[i]=(b[i]-dp[i])/b[0]

这里如果直接写的话复杂度为o(m^2)会超时,所以需要剪枝:

  1. if (dp[j] == 0) continue;  
  2. if (num[i] == 0) break;  
if (dp[j] == 0) continue;
if (num[i] == 0) break;
直接将m^2的复杂度降为nm

  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <cmath>  
  5. #include <algorithm>  
  6. #include <queue>  
  7. #include <map>  
  8. #define ms(a,b) memset(a,b,sizeof(a))   
  9. using namespace std;  
  10. typedef long long ll;  
  11.   
  12. const int maxn = 1e4 + 100;  
  13.   
  14. ll b[maxn];  
  15. int dp[maxn], num[maxn];  
  16.   
  17. int C(int n, int m)  
  18. {  
  19.     int sum = 1;  
  20.     for (int i = n - m + 1; i <= n; i++) sum *= i;  
  21.     for (int i = 1; i <= m; i++) sum /= i;  
  22.     return sum;  
  23. }  
  24.   
  25. int main()  
  26. {  
  27.     int t;  
  28.     scanf("%d", &t);  
  29.     while (t--)  
  30.     {  
  31.         int n, m;  
  32.         scanf("%d%d", &n, &m);  
  33.         ms(dp, 0);  
  34.         ms(num, 0);  
  35.         for (int i = 0; i <= m; i++)  
  36.         {  
  37.             scanf("%lld", &b[i]);  
  38.         }  
  39.         num[0] = log2(b[0]);  
  40.         num[1] = b[1] / b[0];  
  41.         dp[0] = b[0];  
  42.         for (int i = 0; i <= m; i++)  
  43.         {  
  44.             for (int j = m; j >= 0; j--)  
  45.             {  
  46.                 if (dp[j] == 0) continue;  
  47.                 if (num[i] == 0) break;  
  48.                 for (int k = 1; k <= num[i]; k++)  
  49.                 {  
  50.                     if (j + k*i <= m)  
  51.                     {  
  52.                         dp[j + k*i] += dp[j] * C(num[i], k);  
  53.                     }  
  54.                 }  
  55.             }  
  56.             if (i + 1 <= m)  
  57.             {  
  58.                 num[i + 1] = (b[i + 1] - dp[i + 1]) / b[0];  
  59.             }  
  60.         }  
  61.         bool flag = 0;  
  62.         for (int i = 0; i <= m; i++)  
  63.         {  
  64.             for (int j = 1; j <= num[i]; j++)  
  65.             {  
  66.                 if (!flag) printf("%d", i), flag = 1;  
  67.                 else printf(" %d", i);  
  68.             }  
  69.         }  
  70.         puts("");  
  71.     }  
  72.     return 0;  
  73. }  
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#define ms(a,b) memset(a,b,sizeof(a)) 
using namespace std;
typedef long long ll;

const int maxn = 1e4 + 100;

ll b[maxn];
int dp[maxn], num[maxn];

int C(int n, int m)
{
	int sum = 1;
	for (int i = n - m + 1; i <= n; i++) sum *= i;
	for (int i = 1; i <= m; i++) sum /= i;
	return sum;
}

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, m;
		scanf("%d%d", &n, &m);
		ms(dp, 0);
		ms(num, 0);
		for (int i = 0; i <= m; i++)
		{
			scanf("%lld", &b[i]);
		}
		num[0] = log2(b[0]);
		num[1] = b[1] / b[0];
		dp[0] = b[0];
		for (int i = 0; i <= m; i++)
		{
			for (int j = m; j >= 0; j--)
			{
				if (dp[j] == 0) continue;
				if (num[i] == 0) break;
				for (int k = 1; k <= num[i]; k++)
				{
					if (j + k*i <= m)
					{
						dp[j + k*i] += dp[j] * C(num[i], k);
					}
				}
			}
			if (i + 1 <= m)
			{
				num[i + 1] = (b[i + 1] - dp[i + 1]) / b[0];
			}
		}
		bool flag = 0;
		for (int i = 0; i <= m; i++)
		{
			for (int j = 1; j <= num[i]; j++)
			{
				if (!flag) printf("%d", i), flag = 1;
				else printf(" %d", i);
			}
		}
		puts("");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值