POJ 1276 Cash Machine (多重背包)

来源:互联网 发布:网络许可 编辑:程序博客网 时间:2024/05/21 10:32

题目大意

  • 给定cash,N,表示有cash现金,N种账单。每种账单的额度为D[i],数目为n[i],求在不超过cash的前提下,最多能兑换到到少money。

分析

  • 对于每种额度的账单,可选择k张(0 , 1 , … , n[i]),很明显的多重背包问题。
  • 状态:dp[j]表示在剩余j的现金下,前i种账单最多能兑换到的money
  • 状态转移方程: dp[j] = max(dp[j] , dp[j-D[i]]+D[i])
  • 时间复杂度为O(cash*N*n[i]),所以直接写妥妥超时
  • 用状态压缩的办法,把每种账单的数量转化为1 , 2 , … , n[i] - 2^k + 1
  • 时间复杂度为O(cash*N*log(n[i))

代码

/* 题目大意:给定cash,N,表示有cash现金,N种账单 * 每种账单的额度为D[i],数目为n[i] * 求在不超过cash的前提下,最多能兑换到到少money * 方法:多重背包 * dp[j]表示在剩余j的现金下,前i种账单最多能兑换到的money * dp[j] = max(dp[j] , dp[j-D[i]]+D[i]) */#include <iostream>#include <cstring>using namespace std;const int maxn = 100010;int dp[maxn];int z_D[maxn];int num[15] , D[15];int main(){    int cash , n;    while(cin >> cash >> n)    {        for(int i = 0; i < n; i++) cin >> num[i] >> D[i];        if(!cash || !n) {cout << 0 << endl; continue;}        memset(dp , 0 , sizeof(dp));        int cnt = 0;        //状态压缩        for(int i = 0 , k; i < n; i++) {            for(k = 1; k * 2 <= num[i]; k *= 2) z_D[cnt++] = k * D[i];            z_D[cnt++] = (num[i] - k + 1) * D[i];        }        //状态转移方程        for(int i = 0; i < cnt; i++) {            for(int j = cash; j >= z_D[i]; j--) dp[j] = max(dp[j] , dp[j-z_D[i]]+z_D[i]);        }        cout << dp[cash] << endl;    }    return 0;}
0 0
原创粉丝点击