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;
}
posted @ 2025-02-21 16:02  ToFuture$  阅读(20)  评论(0)    收藏  举报