给定一堆硬币,从中挑选出总和为k的组合,求所有组合中能由组合里的硬币组成的金额。
背包dp,但背包不同一般的01背包,这个背包还要看组成的某个金额是否可行,所以需要在01背包的基础上增加一维。dp[i][j]表示当前选择了总和为i的物品,是否能凑出价值为j的组合
状态转移即遍历物品,对每个状态可以有:
1.选用这个物品,把他加入凑的组合内,dp[j][k]|=dp[j-v[i]][k-v[i]]
2.选用这个物品,但不把他加入凑的组合内,dp[j][k]|=dp[j-v[i]][k]
3.不选用这个物品,dp[j][k]|=dp[j][k]原封不动,所以只需要考虑前面两种
初始化状态dp[0][0]=true
#include<bits/stdc++.h>
using namespace std;
int read()
{
int ret=0,base=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') base=-1;
ch=getchar();
}
while(isdigit(ch))
{
ret=(ret<<3)+(ret<<1)+ch-48;
ch=getchar();
}
return ret*base;
}
int n,v[505],m;
int dp[505][505];
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++) v[i]=read();
dp[0][0]=true;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)//01背包倒序枚举
{
for(int k=m;k>=0;k--)//01背包倒序枚举
{
if(k-v[i]>=0) dp[j][k]|=dp[j-v[i]][k-v[i]];
dp[j][k]|=dp[j-v[i]][k];
}
}
}
set<int>ans;
for(int i=0;i<=m;i++)
{
if(dp[m][i]) ans.insert(i);
}
printf("%d\n",ans.size());
for(auto i:ans) printf("%d ",i);
return 0;
}