poj3260 The Fewest Coins

本文详细阐述了使用多重背包和完全背包算法解决FJ同学购物时最小化金币变化数量的问题,包括问题背景、算法设计、代码实现及示例解析。

题意:FJ同学去买东西,东西的价值为T,他和卖家都有N种金币,FJ希望交易完成时金币变化最小。

求最少的金币变化数量。FJ的金币个数有限,卖家的金币数目无限。

思路:背包问题,FJ的每种金币个数有限可以看做是多重背包问题,卖家的金币数目无限可以看做是完全背包问题。

设DP1[i]为FJ付款为i时的最小金币数,设DP2[i]为卖家找钱为i时的最小金币数。

则DP1[i+T]+DP2[i]就是所求的最小金币变化数量(DP1用多重背包求解,DP2用完全背包求解)

PS:这里的背包求得是最小价值,且要恰好装满。故初始化数组时应 DP[0]=0,DP[1~MAXN]=INT_MAX;

/*******************
 * Author;fisty
 * Data:2014-11-18
 * poj3260
 *完全背包+分组背包
 *******************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAX_N  224000
#define INF 1000000000
int dp1[MAX_N],dp2[MAX_N];
int v[120], c[10000];
int n,m;
int m_sum = 0;//FJ可以付的最多钱数

int  solve(){
        for(int i = 0;i <= m_sum; i++){
                dp2[i] = INF;
                dp1[i] = INF;
        }
        dp1[0] = 0;
        dp2[0] = 0;
        //对payment实行分组背包
        for(int i = 1;i <= n; i++){
                if(v[i]*c[i] >= m_sum){
                        for(int j = v[i]; j <= m_sum; j++){
                                dp1[j] = min(dp1[j], dp1[j-v[i]] + 1);
                        }
                }else{
                        int k = 1;
                        while(k < c[i]){
                                for(int j = m_sum; j >= k*v[i]; j--){
                                        dp1[j] = min(dp1[j], dp1[j-k*v[i]] + k);
                                }
                                c[i] -= k;
                                k *= 2;
                        }
                
                        for(int j = m_sum; j >= c[i]*v[i]; j--){
                                dp1[j] = min(dp1[j], dp1[j-v[i]*c[i]] + c[i]);
                        }
                }
        }
        //对charge进行完全背包
        for(int i = 1;i <= n; i++){
                for(int j = v[i]; j <= m_sum; j++){
                        dp2[j] = min(dp2[j-v[i]]+1, dp2[j]);
                }
        }
        int ans = INF;
        for(int i = 0;i <= m_sum - m; i++){
                ans = min(ans, dp1[i+m] + dp2[i]);
        }
        return ans == INF ? -1 : ans;
}
int main(){
        scanf("%d%d", &n, &m);
        for(int i = 1;i <= n; i++){
                scanf("%d", &v[i]);
                m_sum = max(m_sum, v[i]);
        }
        for(int i = 1;i <= n; i++){
                scanf("%d", &c[i]);
        }
        m_sum = m_sum * m_sum + m + 1;
        int ans = solve();
        printf("%d\n", ans);

        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值