poj 1787 Charlie's Change

思路: 多重背包
分析:
1 题目给定的数据明显就是多重背包,但是题目有个难点就是输出路径,0/1背包里面有输出路经的方法,做法做法是通过记录前面的状态
2 这一题还要求输出每种硬币的个数,那么这边利用mark数组,mark[i][0] = pos 表示的是当前这个状态是取的是第pos个物品,mark[i][pos] = x;表示的是当前这个状态增加了x个物品

代码:


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 10010;
const int N = 5;
const int INF = 0x3f3f3f3f;

int v[N] = {0,1,5,10,25};
int sum , num[N] , dp[MAXN];
int ans[N] , path[MAXN] , mark[MAXN][N];

void zeroOnePack(int pos , int k , int cost , int weight){
    for(int i = sum ; i >= cost ; i--){
        dp[i] = max(dp[i] , dp[i-cost]+k);
        if(dp[i] == dp[i-cost]+1){
            path[i] = i-cost;
            mark[i][0] = pos;
            mark[i][pos] = k;
        }
    }
}

void completePack(int pos , int cost , int weight){
    for(int i = cost ; i <= sum ; i++){
        dp[i] = max(dp[i] , dp[i-cost]+1);
        if(dp[i] == dp[i-cost]+1){
            path[i] = i-cost;
            mark[i][0] = pos;
            mark[i][pos] = 1;
        }
    }
}

void multiplePack(int pos , int cost,int weight,int amount){
    if(cost*amount >= sum){
        completePack(pos , cost , weight);
        return;
    }
    int k = 1;
    while(k < amount){
        zeroOnePack(pos , k , k*cost , k*weight); 
        amount -= k;
        k *= 2; 
    }
    zeroOnePack(pos , amount , amount*cost , amount*weight);
}

int main(){
    while(scanf("%d",&sum)){
        scanf("%d%d%d%d",&num[1],&num[2],&num[3],&num[4]);
        if(!(sum+num[1]+num[2]+num[3]+num[4]))
            break;
        for(int i = 0 ; i < MAXN ; i++)
            dp[i] = -INF;
        dp[0] = 0;
        for(int i = 1 ; i <= 4 ; i++)
            multiplePack(i , v[i],v[i],num[i]);
        if(dp[sum] < 0)
            puts("Charlie cannot buy coffee.");
        else{
            int pos = sum;
            memset(ans , 0 , sizeof(ans));
            while(pos){
                int tmp = mark[pos][0];
                ans[tmp] += mark[pos][tmp]; 
                pos = path[pos];
            }
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[2],ans[3],ans[4]);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值