洛谷P1021(邮票面值设计)

洛谷P1021(邮票面值设计)

题目

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

输入格式

2个整数,代表N,K。

输出格式

2行。第一行若干个数字,表示选择的面值,从小到大排序。
第二行,输出“MAX=S”,S表示最大的面值。

解题思路

  首先连续的数字里必定有1,所以邮票面值里必定有1,他要连续,下一张邮票的面值必定是1+1到1n+1里面选,同样地类推,如果我某一步取了x,那么下一步就必定从x+1到xn+1中选,那么不难看出,这里是一个深度搜索的过程,也就是我们说的dfs。
  在dfs过程中,如果我们搜索到底部了,也就是说搜索了k层,那么这时候就开始把这条搜索路上的所有节点,也就是可能面值拿去动态规划,看看它们组成的连续数怎么样,如果它们能组成的连续数比之前的都大,那么就暂时选择它,但是还要观察,如果后面出现的连续数更加大,那么就把它替换掉。
  其实深度搜索的方法大家应该都知道,只是递归不递归的区别而已,那么动态规划呢?我们可以看看,我们建立一个数组,int f[50000],f[i]就表示组成一个总面值为i最少需要几张邮票。在规划过程中,如果已经有了面值为i的邮票,那么f[i]就为1,如果没有,那么就算总面值为i的邮票必定能分成两部分邮票,那么向下求两部分邮票分别所需的最小有票数。比如我有1和3两种面值的邮票,允许最多贴三张邮票。因为已经有面值为1的邮票了,所以f[1]=1,但是没有面值为2的邮票,所以f[2]=f[1]+f[1]=2,同理,
f[3]=1;
f[4]=min(f[1]+f[3],f[2]+f[2])=min(2,4)=2;
f[5]=min(f[1]+f[4],f[2]+f[3])=min(3,3)=3;
f[6]=min(f[1]+f[5],f[2]+f[4],f[3]+f[3])=min(4,4,2)=2;
f[7]=min(f[1]+f[6],f[2]+f[5],f[3]+f[4])=min(3,5,3)=3;
f[8]=min(f[1]+f[7],f[2]+f[6],f[3]+f[5],f[4]+f[4])=min(4,4,4,4)=4;
注意看,这里f[8]已经大于k也就是我假设的3了,那么8是不合适的,所以面值1和3的邮票在k=3的情况下max为7。
Talking is cheap,show you my code.

代码

#include<iostream>
#include<algorithm>
#define inf 99999999
using namespace std;
int n;//n张邮票
int k;//k种邮票
int stack[15];//dfs需要
int num = 0;
int ans[15];//输出答案
int maxx = 0;
void dp() {
	int f[50000];
	f[0] = 0;
	int begin = 1;
	while (begin<50000) {
		f[begin] = inf;
		for (int i = 1;i <= num;i++) {
			if (stack[i] == begin) {
				f[begin] = 1;
			}
		}
		if (f[begin] != 1) {
			for (int j = 1;j <= (begin / 2);j++) {
				f[begin] = min(f[begin], f[j] + f[begin - j]);
			}
		}
		if (f[begin] > n) {
			break;
		}
		else {
			begin++;
		}
	}
	begin--;
	if (begin > maxx) {
		maxx = begin;
		for (int i = 1;i <= k;i++) {
			ans[i] = stack[i];
		}
	}
}
int main() {
	cin >> n >> k;
	stack[++num] = 1;
	while (num) {
		if (num < k) {
			stack[num + 1] = stack[num] + 1;
			num++;
		}
		else {
			dp();
			int flag = stack[num];
			stack[num] = 0;
			num--;
			while (num && (flag == (stack[num] * n + 1))) {
				flag = stack[num];
				stack[num] = 0;
				num--;
			}
			if (num) {
				stack[num + 1] = flag + 1;
				num++;
			}
		}
	}
	for (int i = 1;i <= k;i++) {
		cout << ans[i] << " ";
	}
	cout << endl << "MAX=" << maxx << endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值