题目描述
由于uim买了一些辅导书,口袋里只剩M元(M≤10000)。
餐馆虽低端,但是菜品种类不少,有N种(N≤100),第i种卖a_i 元(a_i≤1000)。
由于是很低端的餐馆,所以每种菜只有一份。
小A奉行“不把钱吃光不罢休”,所以他点单一定刚好吧uim身上所有钱花完。
他想知道有多少种点菜方法。
由于小A肚子太饿,所以最多只能等待1秒。
输入格式
第一行是两个数字,表示N和M。
第二行起N个正数a_i (可以有相同的数字,每个数字均在1000以内)。
输出格式
一个正整数,表示点菜方案数,保证答案的范围在int之内
输入样例
4 4
1 1 2 2
输出样例
3
解法
思路
本题与一般的背包问题差别较大:
- 背包问题中f[]代表的是最终的要求的内容,本题是求点菜方法,所以f[]是点菜方法的集合。
- 总钱数是容量,每盘菜都有对应的价格,但是没有对应的价值,本题也不求价值。
- 本题的要求的东西,并不是物品的属性,所以并没有在输入时体现出来,即并不是固定的。
- 当j与w[i]相等时,需要单独处理,因为数组第一行都是0,而不是加1。加1是因为多了一种只买第i盘菜这种情况。
- 本题要求容量要耗尽,而不是普通的装最大价值。
不变的一点是二维数组仍然是横轴为种类,纵轴为容量:
- j>w[i],f[i][j]=f[i-1][j]+f[i-1][j-w[i]]
- j=w[i],f[i][j]=f[i-1][j]+1
- j<w[i],f[i][j]=f[i-1][j]
代码
#include<iostream>
#include<cmath>
using namespace std;
int main(){
int t=0,n=0;
int f[101][10001]={},w[101]={};
cin>>n>>t;
for(int i=1;i<=n;i++){
cin>>w[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=t;j++){
if(j<w[i]){
f[i][j]=f[i-1][j];
}
else if(j==w[i]){
f[i][j]=f[i-1][j]+1;
}
else
f[i][j]=f[i-1][j]+f[i-1][j-w[i]];
}
}
cout<<f[n][t];
return 0;
}