欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

【无码专区12】子集和(背包dp)

发布时间:2023/12/3 45 豆豆
生活随笔 收集整理的这篇文章主要介绍了 【无码专区12】子集和(背包dp) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

此题已自我实现,但仍归于无码专区

本题在考场上就过了,所以难度并不高,发现性质即可。

problem

nnn 个正整数 a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an,他们的和为 mmm。你想对于其每一个子集 SSS,求出他们的和。

给定 2n2^n2n[0,m][0,m][0,m] 之间的和,其中数字 iii 出现了 bib_ibi 次。

求还原 aaa,数据保证有唯一解。

n≤50,m≤10000,1s,128MBn\le 50,m\le 10000,1s,128MBn50,m10000,1s,128MB

my idea

首先就能知道 b0,bmb_0,b_mb0,bm 一定是 111

马上就发现最小的 aia_iai 是没有能被其他数组合出来的情况的,因为他们全是正数!

所以最小的 bi≠0b_i\neq 0bi=0iii,就意味着 aaa 中原来有 bib_ibiiii

然后考虑第二小的 bj≠0b_j\neq 0bj=0jjj,会注意到有可能 bib_ibiiii 可能会组合出 jjj

减去这些组合就是 aaa 中原本有 bj′b_j'bjjjj

发现这就是个背包 dpdpdp 的过程。

容量 mmm,但最多只会背包 nnn 次。

所以跑得很快。

solution

与我的想法相同。

每次找到子集中最小的元素,也就是最小的 bib_ibi 不等于 000iii,然后从背包里删去即可。

删除就是可以理解成逆向执行一下背包中加入元素 xxx 的操作,也就是从小到大,执行 bi−=bi−xb_i-=b_{i-x}bi=bix

code

#include <bits/stdc++.h> using namespace std; #define maxn 55 #define maxm 10005 #define int long long int n, m, cnt; int b[maxm], a[maxn], f[maxm]; int c[maxn][maxn];signed main() {freopen( "subset.in", "r", stdin );freopen( "subset.out", "w", stdout );scanf( "%lld %lld", &n, &m );for( int i = 0;i <= n;i ++ ) {c[i][0] = c[i][i] = 1;for( int j = 1;j < i;j ++ )c[i][j] = c[i - 1][j - 1] + c[i - 1][j];}for( int i = 0;i <= m;i ++ ) scanf( "%lld", &b[i] );f[0] = 1;for( int i = 1;i <= m;i ++ ) {b[i] -= f[i];if( ! b[i] ) continue;for( int j = 1;j <= b[i];j ++ ) a[++ cnt] = i;for( int j = m;j;j -- ) {for( int k = 1;k <= b[i];k ++ )if( j < k * i ) break;else f[j] += f[j - k * i] * c[b[i]][k];}}for( int i = 1;i <= n;i ++ ) printf( "%lld ", a[i] );return 0; }

总结

以上是生活随笔为你收集整理的【无码专区12】子集和(背包dp)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。