HDU6092Rikka with Subset(逆向背包)

来源:互联网 发布:js在线编辑器 编辑:程序博客网 时间:2024/06/15 19:31

题意:

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


题解:

如果 B_iBi 是 BB 数组中除了 B_0B0 以外第一个值不为 00 的位置,那么显然 ii 就是 AA 中的最小数。

现在需要求出删掉 ii 后的 BB 数组,过程大概是反向的背包,即从小到大让 B_j-=B_{j-i}Bj=Bji

时间复杂度 O(nm)O(nm)

PS:理解一下之后其实就是一个逆向的多重背包

#include<bits/stdc++.h>using namespace std;#define pii pair<int, int>typedef long long ll;const int maxn = 10105;ll b[maxn];int ans[55];int main(){    int t, n, m;    scanf("%d", &t);    while(t--)    {        scanf("%d%d", &n, &m);        for(int i =  0; i <= m; i++)            scanf("%I64d", &b[i]);        int cnt = 0;        for(int i = 1; i <= m; i++)        {            if(b[i] == 0) continue;            if(cnt == n) break;            ans[cnt++] = i;            for(int j = i; j <= m; j++)                b[j] -= b[j-i];            i--;        }        for(int i = 0; i < n; i++)            printf("%d%c", ans[i], i == n-1 ? '\n' : ' ');    }    return 0;}

容易理解点的代码:

#include<bits/stdc++.h>using namespace std;#define pii pair<int, int>typedef long long ll;const int maxn = 10105;ll b[maxn], dp[maxn];int ans[55];int main(){    int t, n, m;    scanf("%d", &t);    while(t--)    {        scanf("%d%d", &n, &m);        for(int i =  0; i <= m; i++)            scanf("%I64d", &b[i]), dp[i] = 0;        dp[0] = 1;        int cnt = 0;        for(int i = 1; i <= m; i++)        {            int num = b[i] - dp[i];            if(cnt == n) break;            for(int j = 0; j < num; j++)            {                ans[cnt++] = i;                for(int k = m; k >= i; k--)                    dp[k] += dp[k-i];            }        }        for(int i = 0; i < n; i++)            printf("%d%c", ans[i], i == n-1 ? '\n' : ' ');    }    return 0;}