hdu 6092 Rikka with Subset(逆向01揹包+思維)

參考:http://www.cnblogs.com/stepping/p/7324970.html

Rikka with Subset

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1122 Accepted Submission(s): 541
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

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
2017 Multi-University Training Contest - Team 5
Recommend
liuyiding | We have carefully selected several similar problems for you: 6095 6094 6093 6092 6091

題目大意:
有一個數列 a[] ,長度(n<=50)。b[i] 表示元素和爲 i 的集合個數。給你一個數列 b[] ,長度(m<=10000),讓你求 a[],並按照其字典序最小輸出。
題解:
網上有說是01揹包,然而我也不知道自己寫了點什麼,大概是01揹包吧
正常的揹包:告訴你這個重量的物品個數,讓你裝包
逆向揹包:這題告訴你,得到的這個重量有多少種,求各個重量的物品個數

官方題解:
1008 Rikka with Subset
簽到題,大致的思想就是反過來的揹包。

如果 B​i​​ 是 B 數組中除了 B​0​​ 以外第一個值不爲 0 的位置,那麼顯然 i 就是 A 中的最小數。

現在需要求出刪掉 i 後的 B 數組,過程大概是反向的揹包,即從小到大讓 Bj-=B​(j−i)​​。

時間複雜度 O(nm)。

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<string>
#include<map>
#include<vector>
using namespace std;
int t,k;
int  n,m;
int bb[10005],b[10005],a[10005];
int main()
{
    scanf("%d",&t);

    while(t--)
    {
       scanf("%d%d",&n,&m);
       memset(a,0,sizeof(a));//a[i]表示第i個數字的個數
       memset(bb,0,sizeof(bb));//bb[k]表示當1~k-1中數字個數確定後,湊到和爲k的種數,不夠就表示,需要單獨k這個數字來湊

       for(int i=0;i<=m;i++)
           scanf("%d",&b[i]);

       k=1; bb[0]=1;
       while(k<=m)
       {
           a[k]=b[k]-bb[k]; //這就是因爲1~k-1個數確定後,能湊到和爲k的種數,不夠的說明a序列中有b[k]-bb[k]個數的k

            for(int j=1;j<=a[k];j++)
            {
                for(int i=m;i>=k;i--) //反着來,避免已經加到結果裏的數字再加一遍,這裏有01揹包的感覺
                   bb[i]+=bb[i-k];
            }
           k++;
       }

       int tot=0;  //輸出
       for(int i=1;i<=m;i++)
          for(int j=1;j<=a[i];j++)
           {
               if (tot++) printf(" ");
               printf("%d",i);
           }
       printf("\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章