找钱问题

找钱问题描述:

与背包问题不同,找钱问题是结果必须是把容量全部装满

一.用的钱的最大最小数目

把空间开大,所需求的dp值只是其中的一种特殊情况而已

1.01背包模型,每种货币只能用一次
最小求法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[50000],w[100];
const int MAX = 50000;
const int inf = 0x3f3f3f3f;
int n,m;
int main()
{
    while(~scanf("%d%d",&n,&m)){
        for(int i = 1;i <= n ; i++)
            scanf("%d%d",&w[i]);
        for(int i = 1; i <= MAX ;i++)
            dp[i] = inf;
        dp[0] = 0;
        for(int i = 1; i <= n; i++){
            for(int j = MAX ; j >= w[i] ;j--){
                dp[j] = min(dp[j],dp[j-w[i]]+1);
            }
        }
        if(dp[m] == inf) printf("-1\n");
        else printf("%d\n",dp[m]);
    }
    return 0;
}

最大求法   只要将初始化变成-1,min改成max

2.完全背包模型,每种货币无限使用

把第二个for循环倒一下就行

3.多重背包模型,每一种货币都有数目限制

1.用完全背包和01背包

2.只用01背包*

用二进制来降低复杂度,应为所有的情况都可以用这些二进制的组合来表示,放大拆开,只要放大取出就行

最小求法,最大同理

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[50000],w[100],c[100];
const int MAX = 50000;
const int inf = 0x3f3f3f3f;
int n,m;
void oneback(int cost,int cnt)
{
    for(int i = MAX; i >= cost; i--)
        dp[i] = min(dp[i-cost]+cnt, dp[i]);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i = 1; i <= n ; i++)
            scanf("%d%d",&w[i],&c[i]);
        for(int i = 1; i <= MAX;i ++)
            dp[i] = inf;
        dp[0] =0;
        int k;
        for(int i = 1; i <= n ; i++){
            k = 1;
            while(k < c[i]){
                oneback(k*w[i],k);
                c[i] -= k;
                k*= 2;
            }
            oneback(w[i]*c[i],c[i]);
        }
        if(dp[m] == inf) printf("-1\n");
        else printf("%d\n",dp[m]);
    }
return 0;
}

4.多重背包模型,钱多了要找回(找回的时候是完全背包,不限数目)

最小求法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[50000],w[100],c[100];
const int MAX = 50000;
const int inf = 0x3f3f3f3f;
int n,m;
void oneback(int cost,int cnt)
{
    for(int i = MAX; i >= cost; i--)
        dp[i] = min(dp[i-cost]+cnt, dp[i]);
}
void comback(int cost,int cnt)
{
    for(int i = MAX+cost ; i >= 0 ; i--)
        dp[i] = min(dp[i-cost]+cnt,dp[i]);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i = 1; i <= n ; i++)
            scanf("%d%d",&w[i],&c[i]);
        for(int i = 1; i <= MAX;i ++)
            dp[i] = inf;
        dp[0] =0;
        int k;
        for(int i = 1; i <= 2*n ; i++){//这样写方便,没有什么特殊的含义。。
            if(i <= n){
            k = 1;
            while(k < c[i]){
                oneback(k*w[i],k);
                c[i] -= k;
                k*= 2;
            }
            oneback(w[i]*c[i],c[i]);
            }
        }
        else comback(-w[i-n],1);
    }
        if(dp[m] == inf) printf("-1\n");
        else printf("%d\n",dp[m]);
    }
return 0;
}

完全背包是从小到大的,但是负数,把循环方向改变一下。循环条件的该表只是要适应下面dp值的改变

5.记录路径的多重背包模型(完全背包和01背包只要把if条件改一下)

最大求法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[10000],w[10000],num[10000],t[10000],path[10000],ans[10000];
const int MAX = 50000;
const int inf = 0x3f3f3f3f;
int n,m;
int main()
{
    while(~scanf("%d%d",&n,&m)){
        for(int i = 1; i <= n ; i++)
            scanf("%d%d",&w[i],&t[i]);
        memset(dp,0,sizeof(dp));
        dp[0] = 1;
        for(int i = 1;  i <= n ; i++){
            for(int j = w[i]; j <= m; j++){
                if(dp[j-w[i]] && dp[j-w[i]] + 1 > dp[j] && num[j-w[i]] < t[i]){
                    dp[j] = dp[j-w[i]];
                    num[j] = num[j-w[i]] + 1;
                    path[j] = j - w[i];
                }
            }
        }
        int i = m;
        if(dp[m] > 0){
            while(i!=0){
                ans[i-path[i]]++;
                i = path[i];
            }
           for(int i = 1; i <= n ;i++){
               if(ans[i]){
                printf("%d:%d ",i,ans[i]);
               }
           }
        }
    }
    return 0;
}

path记录的是当前有j钱的时候买了一件东西之后的最优路径,因为完全背包最后解肯定是最优的所以倒推是正确的

dp只是用来判断,那个值并不能用

最小求法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int v[5] = {0,1,5,10,25};
int dp[10010],ans[10010],num[10010],path[10010],t[5];
int p;
const int inf = 0x3f3f3f3f;
int main()
{
    while(~scanf("%d",&p)){
        for(int i = 1; i <= 4;  i++)
            scanf("%d",&t[i]);
        if((p+t[1]+t[2]+t[3]+t[4]) == 0) break;
        memset(ans,0,sizeof(ans));
        memset(path,0,sizeof(path));
        for(int i = 1; i <= 10010; i++)
            dp[i] = inf;
        dp[0] = 1;
        for(int i = 1; i <= 4; i++){
            memset(num,0,sizeof(num));
            for(int j = v[i]; j <= p; j++){
                if(dp[j-v[i]] != inf && dp[j-v[i]] + 1 < dp[j] && num[j-v[i]] < t[i]){
                    dp[j] = dp[j-v[i]] + 1;//使得后面每一个状态都是从前面一个得到,并且满足两个条件1:用去一个后的数目要比没用去的多2:用去的硬币数目不超过硬币本身
                    num[j] = num[j-v[i]] + 1;
                    path[j] = j - v[i];//难想到。。用来记录路径
                }
            }
        }
        int i = p;
        if(dp[p]!=inf){
            while(i!=0){
                ans[i-path[i]]++;//i-path[i] = i - ( i - v[i]) = v[i]
                i = path[i];//path[i]表示有i钱的时候用了一种硬币之后的钱的数目
            }
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]);
        }
        else printf("Charlie cannot buy coffee.\n");
    }
    return 0;
}

6.

  

时间: 2024-10-28 14:37:09

找钱问题的相关文章

电影院买票问题-&gt;排队找钱

问题:有2n个人排队进电影院,票价是50美分.在这2n个人当中,其中n个人只有50美分,另外n个人有1美元(纸票子).愚蠢的电影院开始卖票时1分钱也没有.问:有多少种排队方法使得每当一个拥有1美元买票时,电影院都有50美分找钱 注:1美元=100美分拥有1美元的人,拥有的是纸币,没法破成2个50美分 解析:符合卡特兰数( Catalan数),因此直接可以得出答案:(2n)/(n!*(n+1)!). Catalan数相关:令h(0)=1,h(1)=1: 1.catalan数满足递推式: h(n)=

POJ3181——DP(找钱3)——Dollar Dayz

Description Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. During his first visit, the tools are selling variously for $1, $2, and $3. Farmer John has exactly $5 to spend. He can buy 5 tools at $1

找钱方式:递归,循环的解法

题目:给定一个金额m,以及几种钱币面值(比如1,2,5),求m有多少种找钱方式 解答: a(m, c): 金额m的找钱方式,此时最大钱币面值为c a(m, c) = sigma( a(m - k*c, next_c) ); k=0~m/c, next_c=比c小的下一个面值的钱币,比如c=5, next_c = 2 按照以上递推式,可以写出递归函数: int exchangeWays(int money, int maxCurIndex = currencyNum - 1) { if (mone

硬币找钱问题,求所有可能解决方案数目,最少的钱币数目,每种钱币用多少张

[题目简介] 现存在一堆面值为 V1.V2.V3 - 个单位的硬币,一共有多少种找钱方法可以找出总值为 T 个单位的零钱?最少需要多少张钱币?在最少钱币数目找钱的条件下,每种钱币使用的次数是多少?假设这一堆面值分别为 1,2,3 元,需要找出总值 T 为 4 元的零钱.很明显,一共有4中找钱方法:1 + 1 + 1 + 1, 2 + 2, 1 + 3, 2 + 2 + 1 + 1:最少需要两张钱币,可能为 3 + 1或者2 + 2.当存在多种情况时,只需要输出其中一种. [题目参数] int c

python多线程实现售票

我们使用mutex(Python中的Lock类对象)来实现线程的同步: lock.acquire() 相当于P操作,得到一个锁,锁定lock.release()相当于V操作,释放一个锁,释放 1 # -*- coding: cp936 -*- 2 import threading # Python主要通过标准库中的threading包来实现多线程 3 import time 4 import os 5 6 7 def doChore(): #作为间隔 每次调用间隔0.5s 8 time.slee

数据结构总结

剑指OfferJAVA版 1.      排序算法 稳定性的概念: 假定待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,称这种排序算法是稳定的,否则称为不稳定的. package com.ljl.sort; import org.junit.Test; /** * 七大排序算法 * @author acer * */ public class Sort { private int[] unsorted={1,3,2,8,9,7,6,6,5,3,4};

从“为什么创业”到“怎么创业”(转)

之前,YC(Y Combinator,美国最成功的创业孵化机构)和 Stanford 联合办了一个创业课,这篇文章的内容就是根据前两节课总结而来(主讲人是YC的掌门人Sam Altman),都是非常基础的一些创业须知的点,发在这里做个留存. 首先从为什么要创业入手,这个问题说简单也简单,说复杂也复杂.YC 创业课总结了三个最常见的创业原因,并一一提供了基于现实情况的反驳.比如: 1)很多人想自己做老板,觉得现在老板做的事和方法看不过去.但事实是,创业后,每个人都是你老板.你要讨好你员工,初创企业

[LeetCode]Integer to Roman

int型数字转化为罗马数字的形式 思路: 由于只是1到3999,一共只有四位,分别求这四位的情况. 可以将每一位从1到9,int和罗马数字的一一对应的关系给出来,然后直接转换. /***************************************************************** Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range fr

2016 年末 QBXT 入学测试

P4744 A’s problem(a) 时间: 1000ms / 空间: 655360KiB / Java类名: Main 背景 冬令营入学测试题,每三天结算一次成绩.参与享优惠 描述 这是一道有背景的题目,小A也是一个有故事的人.但可惜的是这里纸张太小,小A无法把故事详细地说给大家听.可能小A自己也讲不清楚自己的故事,因为如果讲清了,也就没有这道题目了…… 小A的问题是这个样子,它找到了n份不同的工作,第i份工作每个月有ai的工资,每份工作需要小A每天工作8小时,一周工作7天.小A想知道性价