最小邮票数

题目描述:

    有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。
    如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。

输入:

    有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N〈20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。

输出:

      对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。

样例输入:
10
5
1 3 3 3 4
样例输出:
3

算法分析

动态规划问题,和之后的两船载物、今年暑假不AC、招聘会、热爱生活(发大米)、DOTA等均为同一类型题目,背包问题。

在该题中采用动态规划,跟0-1背包问题一样,知道每个邮票的值,求恰好满足value的最小个数,只是这里求的是个数不是最大value,计算从1到m面值的最小邮票数 。更新如下

for(int j = m;j>=num[i];j--){ //must from m to num[i]  
    dp[j] = std::min(dp[j],dp[j-num[i]]+1);  

dp[j]表示总值是j时,需要的邮票个数,邮票i分两种情况:

1、放进去  则邮票个数 dp[j-num[i]]+1

2、不放进去 则邮票个数 dp[j]

所以在dp[i][j]时的最优结构是 dp[i][j]=min(dp[i-1][j],dp[i][j-num[i]+1,转换为一维矩阵就是dp[j]=min(dp[j],dp[j-num[i]]+1)


因为是恰好是某一个值,初始化时,dp[0]=0,  dp[1......V]=INT_MIN;

#include <stdio.h>
#define INF 1000
int main()
{
    int f[1000];       // 邮票价值为i时的个数 
    int i,j,k;
    int M,N;
    int v[21];
    while(scanf("%d %d",&M,&N)!=EOF){
        for(i=1;i<101;i++) f[i]=INF;         // 初始化 
        for(i=1;i<=N;i++) scanf("%d",&v[i]); // 邮票大小 
        f[0]=0;
        for(i=1;i<=N;i++)       // 循环每个邮票 
            for(j=M;j>=0;j--)   // 循环总价值 
                if(j-v[i]>=0) f[j] = f[j-v[i]]+1 > f[j] ? f[j] : f[j-v[i]]+1;   // 取f[j-v[i]]+1和f[j]的最小值 
        if(f[M]>=101) printf("0\n");
        else printf("%d\n",f[M]);
    }
    return 0;
}
或者
void minNum(int m,int n){
    int *dp = new int[m+1];
    dp[0] = 0;
    for(int i = 1;i<=m;i++)
        dp[i] = INF;
    int *num = new int[n];
    for(int i = 0;i<n;i++)
        std::cin>>num[i];
    for(int i = 0;i<n;i++){
        for(int j = m;j>=num[i];j--){ //must from m to num[i]
            dp[j] = std::min(dp[j],dp[j-num[i]]+1);
        }
    }
    if(dp[m]<INF)
        std::cout<<dp[m]<<std::endl;
    else
        std::cout<<0<<std::endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值