hdu 6092 Rikka with Subset 背包dp

来源:互联网 发布:医疗his软件 编辑:程序博客网 时间:2024/05/17 07:57

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

我们熟悉的一个问题是,给你若干数字,选取其中一些,每个数字只能取一次,求它们的和的不同方案数,那现在这个问题反过来了,同样可以用背包做。

因为可能a数组中有0的出现,所以先算出0的个数,也就是log2(b[0]),将这些0放入最后要输出的答案序列中,然后其他位都除以b[0],貌似数据很弱,并没有b[0]不是1的情况出现,之后第一个b[i]不是0的就代表有b[i]个i,然后枚举个数,同时往答案序列中加一个i,并不断小到大更新(和01背包逆序枚举一个道理), d[j]=d[j]-d[j-i]

哎,比赛的时候一直以为是个构造题。。。

#include <iostream>  #include <cstdio>  #include <ctime>  #include <cstring>  #include <algorithm>  #include <vector>  #include <map>  #include <cmath>  #include <set>  #include <queue>  using namespace std;    const int INF=1e9+10;  const double EPS = 1e-10;    typedef long long ll;  const ll mod=998244353;int ans[55],b[10005];int main(){    //freopen("out.txt","w",stdout);int T;scanf("%d",&T);while(T--){int n,m;scanf("%d %d",&n,&m);for(int i=0;i<=m;i++)scanf("%d",&b[i]);int num0=(int)log2(b[0]);int cnt=0;for(int i=0;i<num0;i++) ans[cnt++]=0;for(int i=1;i<=m;i++) b[i]/=b[0];for(int i=1;i<=m;i++){while(b[i]){b[i]--;ans[cnt++]=i;for(int j=i+1;j<=m;j++)b[j]-=b[j-i];}}for(int i=0;i<cnt;i++)printf("%d%c",ans[i],i==cnt-1?'\n':' ' );}    return 0;}


原创粉丝点击