• 【多重背包】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)\)
  • 相关阅读:
    PSP ISO游戏运行必备工具:ISO TOOL 1.970 功能一览&图文教程
    Linux防火墙(原书第3版) 电子书籍
    iptables的相关概念和数据包的流程(图)
    oracle数据库远程连接服务器配置tnsnames
    编程感悟
    工作任务三 打印表单数据
    UltraWebTree的使用心得
    DropDownList应用
    使用UltraWebTree时,如何在刷新后展开之前选中的节点,并绑定相关数据
    webgrid 添加行是不允许相同
  • 原文地址:https://www.cnblogs.com/wdt1/p/13598926.html
Copyright © 2020-2023  润新知