• LFYZ-OJ ID: 1021 邮票问题


    邮票问题

    Problem Description

    设有已知面额的邮票m种,每种有n张,用总数不超过n张的邮票,能从面额1开始,最多连续组成多少面额。(1≤m≤100,1≤n≤100,1≤邮票面额≤255)

    INPUT

    第一行:m,n的值,中间用一空格隔开。
    第二行:A[1..m](面额),每个数中间用一空格隔开。

    OUTPUT

    连续面额数的最大值

    Sample Input

    3 4
    1 2 4

    Sample Output

    14

    分析

    最容易想到的方法是从面额i=1开始计算最少张数,逐渐增加,直到所计算出的张数大于n。最大面额为255,n的最大值为100,所以最大面额不超过25500。意味着最坏情况下需要循环2x104次才能完成,因此算法的效率主要就取决于如何计算构成面额i所需要的最少张数。以三种思路来分析:

    • 用i每次减去m种邮票中面值小于等于i的最大面值,计算减去多少次后等于0。这种思路正确吗?举个反例:有1, 5, 10, 20, 28, 50共6种邮票,现在要构造面值56,按照思路1,是这样的:56-50=6, 6-5=1, 1-1=0,共需要1, 5, 50三张邮票。但你其实可以发现:56-28=28,用两张28便可构成。这证明该思路不正确。
    • 使用递推方法:面额i的最小张数mz[i]=mz[j]+mz[i-j],举个例子:20元可以分为1+19,1元所需要的最小张数加上19元所需要的最小张数就是20元的张数,这次分解得到的值不一定是最小的,需要把所有分解方法枚举一遍:mz[1]+mz[19], mz[2]+mz[18], ..., mz[9]+mz[11], mz[10]+mz[10],枚举的个数为i/2个。最终要的结果为:mz[20]=min{mz[1]+mz[19], mz[2]+mz[18], ..., mz[10]+mz[10]}。这个方法一定无误,问题在于时间复杂度O(n)=i*i/2,时间复杂度可达108,难免会超时,还需要改进。
    • 在思路2的基础上如何改进呢?关键在于分解方法低效,一元一元分解并无意义,完全可以按照m种邮票的面额进行分解。例如有1, 5, 10, 20, 28, 50共6种邮票,现在要构造面值56,可以这样分解:mz[56]=min{mz[1]+mz[55], mz[5]+mz[51], mz[10]+mz[46], mz[20]+mz[36], mz[28]+mz[28], mz[50]+mz[6]}。照这样分解最多需要m次,该方法同样无误,但可以把时间复杂度降低两个数量级至106

    递推的起点是什么?已有的m种邮票面额yp[j] (j=1...m)所需要的张数mz[yp[j]]=1 (j=1...m),这就是递推的起点,需要的已知条件。

    代码示例

    #include<iostream>
    using namespace std;
    int yp[256];							//邮票面额,不使用yp[0]
    int mz[25601];							//所有面值需要的最少张数,连续面额个数不超256*100 
    int main(){
    	int m, n, i=1;                      //变量i为要计算张数的面额
    	scanf("%d%d", &m, &n);
    	for(int i=1; i<=m; i++){
    		scanf("%d", &yp[i]);            //存储该面额
    		mz[yp[i]]=1;					//该面额只需要1张,亦为递推的临界值 
    	}
    	while(1){                           //i从1开始分析
    		for(int j=1; j<=m; j++){        //递推规则
    			if(i>=yp[j]){
    				int zs=mz[yp[j]]+mz[i-yp[j]];
    				if(mz[i]==0)	mz[i]=zs;
    				else 			if(zs<mz[i])	mz[i]=zs; 
    			}
    		}
    		//printf("%d->%d
    ", i, mz[i]);调试使用 
    		if(mz[i]>n || mz[i]==0) break;	//超出n张邮票
    		i++; 
    	}
    	printf("%d", i-1);					//输出最大连续面额 
    	return 0;
    }
    
  • 相关阅读:
    KVM/QEMU/qemu-kvm/libvirt 概念全解
    OpenStack 实现技术分解 (7) 通用库 — oslo_config
    OpenStack 实现技术分解 (7) 通用库 — oslo_config
    OpenStack 实现技术分解 (6) 通用库 — oslo_log
    OpenStack 实现技术分解 (6) 通用库 — oslo_log
    模拟用户注册功能
    007-解决下载文件【中文文件名】乱码
    006-动态生成验证码Servlet代码模板
    CodingLife的CSS样式整理
    Servlet用户登录功能实现
  • 原文地址:https://www.cnblogs.com/lfyzoi/p/6876228.html
Copyright © 2020-2023  润新知