• 【多重背包】A000_AW_硬币(贪心+dp)


    给定N种硬币,其中第 i 种硬币的面值为Ai,共有Ci个。

    从中选出若干个硬币,把面值相加,若结果为S,则称“面值S能被拼成”。

    求1~M之间能被拼成的面值有多少个。

    输入格式
    输入包含多组测试用例。
    每组测试用例第一行包含两个整数N和M。
    第二行包含2N个整数,分别表示A1,A2,…,AN和C1,C2,…,CN。
    当输入用例N=0,M=0时,表示输入终止,且该用例无需处理。

    输出格式
    每组用例输出一个结果,每个结果占一行。

    数据范围
    1≤N≤100,
    1≤M≤10^5,
    1≤Ai≤10^5,
    1≤Ci≤1000

    输入用例:
    3 10
    1 2 4 2 1 1
    2 5
    1 4 2 1
    0 0
    输出用例:
    8
    4
    

    方法一:贪心+dp

    • 定义状态
      • f[i]=0/1 表示面值为 i 的硬币能/否凑出
      • g[j] 表示凑出面值为 j 的硬币时,使用面值为 v[i] 的次数,这里因为每个硬币都有使用次数限制,故应尽量先使用完一种再用其它硬币(贪心)
    • 思考初始化:
      • f[0]=1
      • g[...]=0
    • 思考状态转移方程
      • f[j]=f[j-v[i]];g[j]=g[j-v[i]]+1,if (!f[j] && f[j-A[i]] && g[j-v[i]<C[i])
    • 思考输出:...
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
        
    int main() {
        std::ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int n,m; 
        while (true) {
            cin>>n>>m; if (n==0 && m==0) break;
            ll A[n], C[n];
            for (int i=0; i<n; i++) cin>>A[i];
            for (int i=0; i<n; i++) cin>>C[i];
            int f[m+5], g[m+5]; memset(f, false, sizeof f);
            f[0]=1;
            for (int i=0; i<n; i++) {
                int x=A[i]; memset(g, 0, sizeof g);
                for (int j=x; j<=m; j++) if (!f[j] && f[j-A[i]] && g[j-x]<C[i]) {
                    f[j]=true, g[j]=g[j-x]+1;
                }
            }
            ll ans=0;
            for (int i=1; i<=m; i++) if (f[i])
                ans++;
            cout << ans << '\n';
        }
        return 0;
    }
    

    复杂度分析

    • Time\(O(nm)\)
    • Space\(O(n+m)\)
  • 相关阅读:
    JTAG的SWD接线方式
    Qt のEXecl
    人脸识别
    Qt实现基本QMainWindow主窗口程序
    Qt学习之路MainWindow学习过程中的知识点
    QT_FORWARD_DECLARE_CLASS
    标准的并发控制实现
    C++ DFS
    C# 互操作(一) 编写一个C++ COM组件
    Socket使用SOAP调用WCF
  • 原文地址:https://www.cnblogs.com/wdt1/p/13598926.html
Copyright © 2020-2023  润新知