• POJ 3260 The Fewest Coins(多重背包+全然背包)


    POJ 3260 The Fewest Coins(多重背包+全然背包)

    http://poj.org/problem?id=3260

    题意:

          John要去买价值为m的商品. 如今的货币系统有n种货币,相应面值为val[1],val[2]…val[n]. 然后他身上每种货币有num[i]个. John必须付给售货员>=m的金钱, 然后售货员会用最少的货币数量找钱给John.

    问你John的交易过程中, 他给售货员的货币数目+售货员找钱给他的货币数目 的和最小值是多少?

    分析:

           本题与POJ 1252类型:

           http://blog.csdn.net/u013480600/article/details/40454963

           如果John付款总额为S时的货币数目为T1, 售货员找钱 (S-m) 的货币数目为T2. 我们要使得T1+T2最小, 那么自然T1和T2也必须分别是最小的(T1是当John付款正好S,最少须要多少张货币. T2是当售货员正好找钱S-m,最少须要多少张货币.).

           John给的钱肯定>=m, 可是究竟最大多大呢? 假设我们直接求John的全部金钱总和, 然后再DP, 肯定超时. 这个up_bound (john最多给售货员的钱数) 能够简单设置一个大数值就可以. 网上有个证明(这个证明我也有点不明确):

           John的付款数最多为maxv*maxv+m

           证明例如以下:

           假设John的付款数大于了maxv*maxv+m,即付硬币的数目大于了maxv,依据鸽笼原理。至少有两个的和对maxv取模的值相等(这个意思应该是:至少maxv+1个硬币对maxv求余,然后余数属于[0,maxv-1]范围,肯定有至少两个硬币的余数同样的),也就是说。这部分硬币可以用更少的maxv来取代(这句话我不理解)。

    证毕。

     

           第一个问题是一个多重背包问题.

           令dp[i][j]==x 表示当John用前i种货币组成j元钱时, 最少须要x张货币.

           初始化: dp全为INF(无穷大), 且dp[0][0]=0.

           对于每种货币, 我们分情况对它进行处理:

           1.    假设val[i]*num[i]>=up_bound时, 做一次全然背包.

           2.    假设val[i]*num[i]<up_bound时, 做多次01背包.

           终于所求: dp[n][i] 当中i属于[m, up_bound].

     

           第2个问题是一个全然背包问题.

           令dp2[i][j]==x 表示售货员用前i种硬币组成j元钱时, 最少须要x张货币.

           初始化: dp2全为INF(无穷大), 且dp2[0][0]=0.

           状态转移: dp2[i][j] = max( dp2[i-1][j] , dp2[i][j-val[i]]+1 )

           终于所求: dp2[n][i] 当中i属于[m, up_bound].

     

           终于合并问题1和问题2的解, 我们枚举i从m到up_bound, 找出dp[i]+dp2[i-m]的最小值就可以.

    AC代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define INF 1e9
    
    int n;//n种硬币
    int m;//购买商品的价值
    int up_bound;//DP价值上界
    int val[100+5];//每种硬币价值
    int num[100+5];//每种硬币数目
    int dp[55555]; //多重背包
    int dp2[55555];//全然背包
    
    //1次01背包
    void ZERO_ONE_PACK(int *dp,int cost,int sum)
    {
        for(int i=up_bound;i>=cost;i--)
            dp[i] = min(dp[i], dp[i-cost]+sum);//注意这里是+sum
    }
    
    //1次全然背包
    void COMPLETE_PACK(int *dp,int cost)
    {
        for(int i=cost;i<=up_bound;i++)
            dp[i] = min(dp[i], dp[i-cost]+1);
    }
    
    //1次多重背包
    void MULTIPLY_PACK(int *dp,int cost,int sum)
    {
        if(cost*sum>=up_bound)
        {
            COMPLETE_PACK(dp,cost);
            return ;
        }
    
        int k=1;
        while(k<sum)
        {
            ZERO_ONE_PACK(dp,cost*k,k);
            sum -=k;
            k *=2;
        }
        ZERO_ONE_PACK(dp,cost*sum,sum);
    
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m)==2)
        {
            //读取输入+计算上界
            int max_val=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&val[i]);
                max_val= max(max_val,val[i]);
            }
            for(int i=1;i<=n;i++)
                scanf("%d",&num[i]);
            up_bound=max_val*max_val+m;//上界
    
            //初始化dp和dp2
            for(int i=1;i<=up_bound;i++)
                dp[i]=dp2[i]=INF;
            dp[0]=dp2[0]=0;
    
            //递推过程
            for(int i=1;i<=n;i++)
            {
                MULTIPLY_PACK(dp,val[i],num[i]);
                COMPLETE_PACK(dp2,val[i]);
            }
    
    
            //统计结果
            int ans=INF;
            for(int i=m;i<=up_bound;i++)
                ans= min(ans, dp[i]+dp2[i-m]);
            printf("%d
    ",ans==INF?-1:ans);
        }
        return 0;
    }
    

  • 相关阅读:
    getElement方法封装
    使用Ajax (put delete ) django原生CBV 出现csrf token解决办法
    (IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)
    协程介绍, Greenlet模块,Gevent模块,Genvent之同步与异步
    Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures
    线程概念( 线程的特点,进程与线程的关系, 线程和python理论知识,线程的创建)
    进程同步控制(锁,信号量,事件), 进程通讯(队列和管道,生产者消费者模型) 数据共享(进程池和mutiprocess.Pool模块)
    在Python程序中的进程操作,multiprocess.Process模块
    进程前戏 (操作系统简述 什么是进程)
    django ModelForm
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7142664.html
Copyright © 2020-2023  润新知