HDU6092Rikka with Subset(逆向背包)

本文介绍了一种逆向多重背包问题的解决方法,通过给定的数列b[]求解数列a[],并确保a[]的字典序最小。文章提供了两种实现思路及代码示例,详细阐述了如何通过反向操作来更新b[]数组,从而得到目标数列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

有一个数列 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值