前置知识 这里
本题已知每种和的个数 存在b数组里
由此我们可以递推求解
b[i]-前面构造成i大小的方法数= i的个数 此值设为变量num
如b[2]-前面构造成2大小的方法数= 2的个数
然后就变成了求前面构造成i大小的方法数的问题
这个就是01背包 a【】数组为物品 价值均为1 重量为a【i】
所以我们有公式
dp【j】【k】 =dp[j-1][k]+dp[j-1][k-a[i]] 由j个物品构造成k 的方法数
由于a[i]是由i从1开始构造 由很多重复 为了节省时间不再多开一次循环 a【i】直接用i代替
就变成:
dp【j】【k】 = dp[j-1][k]+dp[j-1][k-i]
比如 i=1 此时dp【num】【m】就是全部由1构成 大小为m的方法数
i=2 此时dp【num】【m】就是全部由1,2构成 大小为m的方法数
然后再无脑滚动数组一下 就是dp[k]+=dp[k-i]
为啥可以变a[i]为i?
比如构造的a为 1 1 2
现在我要算 dp(3) 在dp(3-2)即dp(1)时 他同时叠加了在dp(1)时 dp(1-0)的状态
讲的好乱....
#include<cstdio>
#include<cstring>
#define clr(a,b) memset(a,b,sizeof(a));
using namespace std;
int a[100];
int b[10010];
int dp[10010];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
clr(dp,0)
clr(a,0)
scanf("%d%d",&n,&m);
for(int i=0;i<=m;i++) scanf("%d",&b[i]);
int cnt=0;
dp[0]=1;
for(int i=1;i<=m;i++)
{
if(cnt==n) break;
int num=b[i]-dp[i];//dp[i]是i-1构造i的个数
for(int j=0;j<num;j++)
{
a[cnt++]=i;
for(int k=m;k>=i;k--)
{
dp[k]+=dp[k-i];
}
}
}
printf("%d",a[0]);
for(int i=1;i<cnt;i++)
printf(" %d",a[i]);
printf("\n");
}
}
结合代码看下吧