题目
给定一个信封,最多只允许粘贴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;
}