zoukankan      html  css  js  c++  java
  • 找钱问题

    找钱问题描述:

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

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

    把空间开大,所需求的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
    ");
            else printf("%d
    ",dp[m]);
        }
        return 0;
    }
    View Code

    最大求法   只要将初始化变成-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
    ");
            else printf("%d
    ",dp[m]);
        }
    return 0;
    }
    View Code

    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
    ");
            else printf("%d
    ",dp[m]);
        }
    return 0;
    }
    View Code

     完全背包是从小到大的,但是负数,把循环方向改变一下。循环条件的该表只是要适应下面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;
    }
    View Code

    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.
    ",ans[1],ans[5],ans[10],ans[25]);
            }
            else printf("Charlie cannot buy coffee.
    ");
        }
        return 0;
    }
    View Code

    6.

      

  • 相关阅读:
    jQuery Ajax同步参数导致浏览器假死怎么办
    自顶而下系统构架分析
    IEnumerable,IQueryable之前世今生
    C#执行存储过程
    JQuery iframe
    跨服务器插入查询数据
    使用游标、存储过程、pivot 三种方法导入数据
    分库分表的面试题3
    分库分表的面试题2
    分库分表的面试题1
  • 原文地址:https://www.cnblogs.com/zero-begin/p/4470752.html
Copyright © 2011-2022 走看看