zoukankan      html  css  js  c++  java
  • P1164 小A点菜题解

    题目传递门

    一、递推思路:

    以中间某个通过状态为样本进行分析,比如我们现在面对第(i)种菜,设(f[i])是前(i)种菜的所有点菜方法,但仔细一想,这样不行,为什么呢?因为只考虑了菜,没考虑!不考虑钱的点菜是没有灵魂的~

    所以前(i)种菜的点菜方法,是受钱数制约的,就是,还有另一个钱数的维度。所以,我们设(f[i][j])给在前(i)种菜,在钱数上限(j)之内的点菜方法数。

    注意:
    本题是二维的,与前一题P2437 蜜蜂路线不一样,那个简单,是一维的。

    (f[i][j])是由哪些状态转移而来呢?我们可以想像,我们面对第(i)个菜,有三种情况:
    一、剩余的钱数正好等于(i)号菜价格
    1、选择购买,买完了就没有钱了,就到头了。加(1)就完事了,游戏到此结束啊!!也可以理解为(base case),就是基础依赖项,开头项。
    (f[i][j]=f[i-1][j]+1)

    2、不选择就是原来的。(f[i][j]=f[i-1][j])

    二、剩余的钱数大于购买(i)号菜价格
    1、选择购买
    (f[i][j]=f[i-1][j-a[i]])

    2、不选择就是原来的。(f[i][j]=f[i-1][j])

    三、剩余的钱数小于购买第(i)号菜价格
    1、没的选,只能是(f[i][j]=f[i-1][j])

    C++代码

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 110;
    const int M = 10010;
    int a[N], f[N][M];
    int n, m;
    
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i)cin >> a[i];
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                //正好相等,方案数+1
                if (j == a[i])f[i][j] = f[i - 1][j] + 1;
                //如果大于,不选,就是原来的方案数;选了,那么就依赖于j-a[i]这些钱在i-1个物品中的方案数
                if (j > a[i]) f[i][j] = f[i - 1][j] + f[i - 1][j - a[i]];
                //如果小于,没的选择
                if (j < a[i]) f[i][j] = f[i - 1][j];
            }
        cout << f[n][m];
        return 0;
    }
    

    二、递归思路

    有时,递归与深度优先我喜欢混淆概念,因为从本质上讲,深度优先就是递归嘛。

    深搜的话,就是一样一个思路:
    1、我一个都没点菜,兜里有钱(m)元。
    2、我看了一眼第一个菜,存在三种可能:
    (1)等于(m)
    如果点了,则钱花光,方法数+1;
    如果不点,继续下一个菜。

    (2)大于(m)
    如果点了,则钱减少;继续点下一个菜。
    如果不点,则继续点下一个菜。

    (3)小于(m)
    不够买,只能放弃。

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 110;
    int a[N];
    int n, m;
    //纯递归思路,最后一个测试点TLE
    
    //x:第几个位置
    //m:钱数上限
    int dfs(int x, int m) {
        int ans = 0;
        if (m == 0) return 1;//钱花光,增加一种办法
        if (x == n) return 0;//选择完n种,钱还没有花光,无效方法
        //看下一个菜,如果钱还够
        if (a[x + 1] <= m)
            ans += dfs(x + 1, m - a[x + 1]); //选择当前位置的菜
        ans += dfs(x + 1, m);  //不选择当前位置的菜
        return ans;
    }
    
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i++)cin >> a[i];
        cout << dfs(0, m) << endl;
        return 0;
    }
    

    因为递归会存在大量重复计算,所以可以采用记忆化的方法记录到二维数组中,减少计算次数:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 110;
    int a[N];
    int n, m;
    int dp[N][10010];
    
    //x:第几个位置
    //m:钱数上限
    int dfs(int x, int m) {
        if (dp[x][m]) return dp[x][m];
        int ans = 0;
        if (m == 0) return 1;
        if (x == n) return 0;
        if (a[x + 1] <= m) ans += dfs(x + 1, m - a[x + 1]); //选择当前位置的菜
        ans += dfs(x + 1, m);  //不选择当前位置的菜
        dp[x][m] = ans;
        return ans;
    }
    
    int main() {
        cin >> n >> m;  
        for (int i = 1; i <= n; i++)cin >> a[i];
        cout << dfs(0, m) << endl;
        return 0;
    }
    
  • 相关阅读:
    【CSS】Intermediate2:Grouping and Nesting
    【CSS】Intermediate1:Class and ID Selectors
    【HTML】Intermediate7:Sectioning
    【HTML】Intermediate6:Text: Addresses, Definitions, Bi-directional, and Editorial
    【HTML】Intermediate5:Definition Lists
    【HTML】Intermediate4:Tables:rowspan and colspan
    【HTML】Intermediate3:Meta Tags
    【HTML】Intermediate2:Text: AbbreviationsQuotations Code
    【HTML】Intermediate1:Span&Div
    【CSS】Beginner6:Border
  • 原文地址:https://www.cnblogs.com/littlehb/p/15026093.html
Copyright © 2011-2022 走看看