P1164 小A点菜——记忆化搜索,递推与递归
题目背景
uim 神犇拿到了 uoi 的 ra(镭牌)后,立刻拉着基友小 A 到了一家……餐馆,很低端的那种。
uim 指着墙上的价目表(太低级了没有菜单),说:“随便点”。
题目描述
不过 uim 由于买了一些书,口袋里只剩 \(M\) 元 \((M \le 10000)\)。
餐馆虽低端,但是菜品种类不少,有 \(N\) 种 \((N \le 100)\),第 \(i\) 种卖 \(a_i\) 元 \((a_i \le 1000)\)。由于是很低端的餐馆,所以每种菜只有一份。
小 A 奉行“不把钱吃光不罢休”的原则,所以他点单一定刚好把 uim 身上所有钱花完。他想知道有多少种点菜方法。
由于小 A 肚子太饿,所以最多只能等待 \(1\) 秒。
输入格式
第一行是两个数字,表示 \(N\) 和 \(M\)。
第二行起 \(N\) 个正数 \(a_i\)(可以有相同的数字,每个数字均在 \(1000\) 以内)。
输出格式
一个正整数,表示点菜方案数,保证答案的范围在 int 之内。
输入输出样例 #1
输入 #1
4 4
1 1 2 2
输出 #1
3
说明/提示
2020.8.29,增添一组 hack 数据 by @yummy
超时解法
for循环暴力&无记忆
#include <bits/stdc++.h>
using namespace std;
int a[1001];
int mycount=0;
int n,m;
void dfs(int index,int money) {
if(money==0) {
mycount++;
return;
}
for (int i=index;i<n;i++) {
dfs(i+1,money-a[i]);
}
}
int main() {
cin>>n>>m;
for (int i=0;i<n;i++) {
cin>>a[i];
}
dfs(0,m);
cout<<mycount;
}
题解
最小事件:选择/不选择;记忆化搜索
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 105;
const int MAXM = 10010;
int a[MAXN];
int n, m;
int mem[MAXN][MAXM];
// 记忆化搜索函数
int dfs(int index, int money) {
// 如果剩余钱为 0,说明刚好花完,返回 1
if (money == 0) {
return 1;
}
// 如果剩余钱小于 0 或者已经遍历完所有菜品,返回 0
if (money < 0 || index == n) {
return 0;
}
// 如果已经计算过该状态,直接返回结果
if (mem[index][money] != -1) {
return mem[index][money];
}
// 不选择当前菜品
int res = dfs(index + 1, money);
// 选择当前菜品
res += dfs(index + 1, money - a[index]);
// 记录当前状态的结果
mem[index][money] = res;
return res;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 初始化记忆化数组
memset(mem, -1, sizeof(mem));
cout << dfs(0, m) << endl;
return 0;
}
浙公网安备 33010602011771号