邮票问题

题目描述

  给定一个信封,最多只允许粘贴N张邮票,计算在给定M(N+M< =10)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大max ,使得1~max之间的每一个邮资值都能得到。
例如,N=3,M=2,如果面值分别为1分、4分,则在l分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分):如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,M=2时,7分就是可以得到连续的邮资最大值,所以MAX=7,面值分别为l分、3分。
样例输入:共一行,两个整数,分表为N与M的值。

输入数据

一行,分别为 N,M 。

输出数据

两行。
第一行为 m 种邮票的面值,按升序排列,各数之间用一个空格隔开。
第二行为最大值。

样例输入

3 2

样例输出

1 3
MAX=7

解析:

1、当邮票面值a[1:M]已知,根据当前邮票可以求出最大maxv ,使得1~maxv之间的每一个邮资值都能得到。

这里的状态方程为:dp[i] 表示 maxv 为 i 所需要的最少的邮票数。 dp[i]=min(dp[i-a[j]]+1),j=(1->M)

for(int j=1;j<=m && a[j]<=i;j++)
    dp[i] = min(dp[i-a[j]]+1, dp[i]);

 2、当前邮票未知,采用深度搜索方式枚举可能的邮票面值组合。a[j] 的枚举的范围是 a[j-1]+1 ~ maxv+1 (其中maxv为 前 j-1 个邮票能组成的最大连续值)

void dfs(int num){
    if(num>= m){ //当收集到m个邮票面值,进行验证
        int c = cale(m); //当前邮票面值组合能够组成的最大连续值
        if(c <= maxv)    return;
        for(int i = 1;i <= m; i++) //当能够到达的连续值比当前maxv大,更新邮票面值
            ans[i] = a[i];
        maxv = c; //更新最大值
        return;
    }else{
        int temp = cale(num); //前num个邮票能够组合的最大连续值
        for(int i = temp + 1;i > a[num]; i--){
            a[num + 1] = i;
            dfs(num+ 1);
        }
    }
}

3、动态规划的实现

int cale(int num){
    if(!num)    return 0;
    memset(dp, 0x7f, sizeof(dp));
    dp[0] = 0;
    int i = 0;
    while(dp[i]<=n){
    	i++;
    	for(int j=1;j<=num && a[j]<=i; j++)
            dp[i] = min(dp[i-a[j]] + 1, dp[i]);
	}
	return i-1;
}

 4、完整代码

#include<iostream>
#include<cstring>

using namespace std;
int a[15];
int ans[15];
int dp[1001];
int n, m;
int maxv = 0;
int cale(int num){
    if(!num)    return 0;
    memset(dp, 0x7f, sizeof(dp));
    dp[0] = 0;
    int i = 0;
    while(dp[i]<=n){
    	i++;
    	for(int j=1;j<=num && a[j]<=i; j++)
            dp[i] = min(dp[i-a[j]] + 1, dp[i]);
	}
	return i-1;
}
void dfs(int num){
    if(num >= m){
        int c = cale(m);
        if(c <= maxv)    return;
        for(int i = 1;i <= m; i++)
            ans[i] = a[i];
        maxv = c;
        return;
    }else{
        int temp = cale(num);
        for(int i = temp + 1;i > a[num]; i--){
            a[num + 1] = i;
            dfs(num + 1);
        }
    }
}
int main(){
    cin >> n >> m;
    dfs(0);
    cout<<ans[1];
    for(int i = 2;i <= m;i++)
        cout<<" "<<ans[i];
    cout<<endl;
    cout<<"MAX="<<maxv;
    return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值