题目链接:http://noi.openjudge.cn/ch0206/2985/

描述
有n个正整数,找出其中和为t(t也是正整数)的可能的组合方式。如:
n=5,5个数分别为1,2,3,4,5,t=5;
那么可能的组合有5=1+4和5=2+3和5=5三种组合方式。
输入
输入的第一行是两个正整数n和t,用空格隔开,其中1<=n<=20,表示正整数的个数,t为要求的和(1<=t<=1000)
接下来的一行是n个正整数,用空格隔开。
输出
和为t的不同的组合方式的数目。

01背包的数量问题和普通01背包问题的不同在于,普通01背包所求的是最大价值的问题,而不考虑是否正好能够购买这些物品。也就是说,购买物品的花费不一定就是背包容量的总和。

01背包的数量问题,就是利用正好能够容纳这个价值的元素进行状态转移,它的转移 =  不考虑当前数的转移  + 考虑当前数的转移。这个数量是正好的。
转换为一维背包,应该从大到小考虑所有“正好容纳”的状态。

二维的初始值的设置需要思想的是,根据数的大小来设置。例如  一个价值5,那么f[i][5] = 1。保证只考虑这个物品的正好数为1种方法。
而一维的初始值设置f[0]为1,因为这里的f[0]对应的是 f[j-v]的正好转移。

code:

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#define ll long long
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int nn = 1010;
int a[25];
int f[25][nn];
int ff[nn];
int main()
{
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n,t,v;
	cin>>n>>t;
	/* 二维方法 
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		f[i][a[i]] = 1;//初始为   物品对应和为1,保证一定有一次和时等于这个数 
	}
	//f[i][j] 表示 前i个数字,总和j的组合的方式的数量 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=t;j++)
		{
			if(j>=a[i])
			f[i][j]+=f[i-1][j]+f[i-1][j-a[i]];
			else f[i][j]+=f[i-1][j];
		}
	*/
	ff[0]=1;
	for(int i=1;i<=n;i++)
	{
		cin>>v;
		for(int j=t;j>=v;j--)
			ff[j]+=ff[j-v];
	} 
	cout<<ff[t]<<endl; 
}