POJ 1742 Coins

题意:现有n种面额的硬币,面额、个数分别为A_i、C_i,要求最多能给出几种不超过m的金额。
分析:这是一个多重部分和问题(多重背包问题),最基本的做法是:

dp[i][j] := 前i种硬币能否凑成j

递推关系式:

dp[i][j]=1 (存在k,使dp[i-1][j-k*a[i]]=1,其中k<=c[i]、k*a[i]<=j)

#include<iostream>

using namespace std;

int dp[105][100005];
int a[105];
int c[105];

int main() {
    int n, m;
    cin >> n >> m;
    memset(dp, 0, sizeof(dp));

    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    for (int i = 0; i < n; ++i) {
        cin >> c[i];
    }

    dp[0][0] = 1;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j <= m; ++j) {
            for (int k = 0; k <= c[i] && k*a[i] <= j; ++k) {
                dp[i + 1][j] |= dp[i][j - k*a[i]];
            }
        }
    }
    int count = 0;
    for (int i = 1; i <= m; ++i) {
        if (dp[n][i]) {
            count++;
        }
    }
    cout << count << endl;

    return 0;
}

dp[i][j] := 用前i种硬币凑成j时,第i种硬币最多剩余数(-1表示配不出来)

  1. 如果dp[i - 1][j] >= 0(前i-1个数可以凑出j,那么第i个数根本用不着)则dp[i][j]=C[i],
  2. 如果j < a[i]或者dp[i][j - a[i]] <=0 (面额太大或者在配更小的数的时候就用光了)dp[i][j] = -1,
  3. 其他(将第i个数用掉一个)dp[i][j] = dp[i][j-a[i]] - 1
#include<iostream>

using namespace std;

int dp[105][100005];
int a[105];
int c[105];

int main() {
    int n, m;
    while ((cin >> n >> m) && n + m) {
        memset(dp, -1, sizeof(dp));

        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
        for (int i = 0; i < n; ++i) {
            cin >> c[i];
        }

        dp[0][0] = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j <= m; ++j) {
                if (dp[i][j] >= 0) {
                    dp[i + 1][j] = c[i];
                }
                else if (j < a[i] || dp[i + 1][j - a[i]] <= 0) {
                    dp[i + 1][j] = -1;
                }
                else {
                    dp[i + 1][j] = dp[i + 1][j - a[i]] - 1;
                }
            }
        }
        int count = 0;
        for (int i = 1; i <= m; ++i) {
            if (dp[n][i] != -1) {
                count++;
            }
        }
        cout << count << endl;
    }

    return 0;
}

利用一维dp数组:

#include<iostream>

using namespace std;

int dp[100005];
int a[105];
int c[105];

int main() {
    int n, m;
    while ((cin >> n >> m) && n + m) {
        memset(dp, -1, sizeof(dp));

        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
        for (int i = 0; i < n; ++i) {
            cin >> c[i];
        }

        dp[0] = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j <= m; ++j) {
                if (dp[j] >= 0) {
                    dp[j] = c[i];
                }
                else if (j < a[i] || dp[j - a[i]] <= 0) {
                    dp[j] = -1;
                }
                else {
                    dp[j] = dp[j - a[i]] - 1;
                }
            }
        }
        int count = 0;
        for (int i = 1; i <= m; ++i) {
            if (dp[i] != -1) {
                count++;
            }
        }
        cout << count << endl;
    }

    return 0;
}

参考:POJ 1742 Coins 图表详解 《挑战程序设计竞赛(第2版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值