动态规划

来源:互联网 发布:人机料法环测测知什么 编辑:程序博客网 时间:2024/06/08 08:22

Rikka with Subset


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


容易想到,0的个数就是log2(b[0]),一的个数就是b[1]/b[0].但是题目明确是正整数,所以1的个数其实就是b[1].广义的讲,第一个不为零的b[i]表示a[ ]数组中i的个数为b[i],以此类推,那么有

j+iji=j+ii
转化为递推式就是 b[j]=b[j]-b[j-i],i是当前b[ ]数组中第一个不为零的b[i].

#include <iostream>#include <stdio.h>using namespace std;const int ma=1e4+10;int b[ma],a[55];int main(){    int t;    scanf("%d",&t);    int n,m;    while(t--)    {        scanf("%d%d",&n,&m);        for(int i=0;i<=m;++i)            scanf("%d",&b[i]);        int cnt=0,i=1;        while(cnt<n)        {            while(!b[i]&&i<=m) ++i;            if(i>m) break;            a[cnt++]=i;            for(int j=i;j<=m;++j)                b[j]-=b[j-i];        }        for(int i=0;i<cnt;++i)            printf("%d%c",a[i],i==cnt-1?'\n':' ');    }    return 0;}

还有另一种写法,是依据公式做的。

如果1的个数为b[1],那么,2的个数就是b[2]-C(b[1],2);

同样3的个数就是b[3]减去{1,1,1},{1,2}的组合数,就是b[3]-C(b[1],3)-C(b[1],1)*C(b[2],1),以此类推。

看一个的AC代码:点击打开链接

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;const int maxn = 1e4 + 100;int b[maxn];int dp[maxn], num[maxn];int C(int n, int m){int sum = 1;for (int i = n - m + 1; i <= n; i++) sum *= i;for (int i = 1; i <= m; i++) sum /= i;return sum;}int main(){int t;scanf("%d", &t);while (t--){int n, m;scanf("%d%d", &n, &m);memset(dp,0,sizeof(dp));memset(num,0,sizeof(num));for (int i = 0; i <= m; i++)scanf("%d", &b[i]);num[0] = log2(b[0]);num[1] = b[1] / b[0];dp[0] = b[0];for (int i = 0; i <= m; i++){for (int j = m; j >= 0; j--){if (dp[j] == 0) continue;if (num[i] == 0) break;for (int k = 1; k <= num[i]; k++){if (j + k*i <= m){dp[j + k*i] += dp[j] * C(num[i], k);}}}if (i + 1 <= m){num[i+1] = (b[i+1] - dp[i+1]) / b[0];}}bool flag = 0;for (int i = 0; i <= m; i++){for (int j = 1; j <= num[i]; j++){if (!flag) printf("%d", i), flag = 1;else printf(" %d", i);}}puts("");}return 0;}