题目背景
uim 神犇拿到了 uoi 的 ra(镭牌)后,立刻拉着基友小 A 到了一家……餐馆,很低端的那种。
uim 指着墙上的价目表(太低级了没有菜单),说:“随便点”。
题目描述
不过 uim 由于买了一些书,口袋里只剩 M 元 (0<M≤10000)。
餐馆虽低端,但是菜品种类不少,有 N 种 (1≤N≤100),第 i 种卖 ai 元 (0<ai≤1000)。由于是很低端的餐馆,所以每种菜只有一份。
小 A 奉行“不把钱吃光不罢休”的原则,所以他点单一定刚好把 uim 身上所有钱花完。他想知道有多少种点菜方法。
由于小 A 肚子太饿,所以最多只能等待 1 秒。
输入格式
第一行两个整数 N 和 M,分别表示菜品种类和 uim 身上的钱数。
第二行 N 个正整数 ai(可能有重复),用空格隔开,分别表示每种菜的价格。
输出格式
一个正整数,表示点菜方案数,保证答案的范围在 [0,231−1] 之内(不超过 C/C++的 int 范围)。
输入输出样例
输入 #1
4 4
1 1 2 2
输出 #1
3
思路:
用二维动态规划,soyo[i][j] 表示前 i 种菜里选出若干种,总价恰好为 j 的方案数。
- 对第 i 道菜,若价格超过 j,则不选它,方案数等于 soyo[i-1][j];
- 若价格小于 j,则方案数为 “不选它的方案数” 加上 “选它(即前 i-1 道菜凑 j-saki[i] 的方案数)”;
- 若价格正好等于 j,则在不选它的方案数基础上再加 1(单独选这道菜的情况)。
代码:
#include <iostream>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
#include <stack>
#include <string>
#include <cmath>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>
#include <array>
#define ll long long
const ll N = 2e6 + 5;
const ll Max = 0x3f3f3f3f;
using namespace std;
ll n, m;
int main() {
cin >> n >> m;
vector<ll>saki(n + 1);
vector<vector<ll>>soyo(n + 1, vector<ll>(m + 1, 0));
for (int i = 1; i <= n; i++)cin >> saki[i];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (saki[i] > j)soyo[i][j] = soyo[i – 1][j];
if (saki[i] < j)soyo[i][j] = soyo[i – 1][j] + soyo[i – 1][j – saki[i]];
if (j == saki[i])soyo[i][j] = soyo[i – 1][j] + 1;
}
}
cout << soyo[n][m];
return 0;
}
