UVa 165 - Stamps(连续邮资问题)

题意:

某国家发行k种不同面值的邮票,并且规定每张信封上最多只能贴h张邮票。 公式n(h,k)表示用从k中面值的邮票中选择h张邮票,可以组成面额为连续的1,2,3,……n, n是能达到的最大面值之和。
例如当h=3,k=2时, 假设两种面值取值为1,4, 那么它们能组成连续的1……6, 虽然也可以组成8,9,12,但是它们是不连续的了。

解析:

    首先开一个数组 stampVal[1...i] 来保存各个面值,再开一个 maxVal[1...i] 来保存当前所有面值能组成的最大连续面值。
    那么,我们可以确定 stampVal[1] 一定是等于1的。因为如果没有1的话,很多数字都不能凑成。
    然后相应的, maxVal[1]=1h
    h为允许贴邮票的数量

接下去就是确定第二个,第三个……第k个邮票的面值了,这个该怎么确定呢?
    对于stampVal[i+1],它的取值范围是 stampVal[i]+1 maxVal[i]+1
stampVal[i]+1 好理解, 这一次取的面值肯定要比上一次的面值大, 而这次取的面值的上限是上次能达到的最大连续面值+1, 是因为如果比这个更大的话, 那么就会出现断层, 即无法组成上次最大面值+1这个数了。 举个例子, 假设可以贴3张邮票,有3种面值,前面2种面值已经确定为1,2, 能达到的最大连续面值为6, 那么接下去第3种面值的取值范围为3~7。如果取得比7更大的话会怎样呢? 动手算下就知道了,假设取8的话, 那么面值为1,2,8,将无法组合出7 !

    知道了这个以后,就可以知道回溯的大概思路了, 但是还不够, 怎样取得给定的几个面值能够达到的最大连续面值呢?

最直观容易想到的就是直接递归回溯枚举所有情况, 便可知道最大连续值了。
这是查询的函数

//pos表示当前的位置,sum表示面额的和,num表示当前的面额数
void check(int pos, int sum, int num) {
    if(pos >= h) {
        vis[sum] = true;
        return;
    }
    vis[sum] = true;
    for(int i = 1; i <= num; i++)
        check(pos+1, sum+stampVal[i], num);
}

vis是一个全局数组, 调用递归时先初始化为0。然后用它来记录出现过的面值之和。
最后只需要从vis数组的下标1开始枚举,直到不是true值时就是能达到的最大连续面值。

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200;
const int INF = 0x3f3f3f3f;
bool vis[N];
int ans[N], stampVal[N], maxVal[N], maxAns;
int h, K;
void check(int pos, int sum, int num) {
    if(pos >= h) {
        vis[sum] = true;
        return;
    }
    vis[sum] = true;
    for(int i = 1; i <= num; i++)
        check(pos+1, sum+stampVal[i], num);
}
void dfs(int curKind) {
    if(curKind >= K) {
        if(maxVal[curKind] > maxAns) {
            maxAns = maxVal[curKind];
            memcpy(ans, stampVal, sizeof(stampVal));
        }
        return;
    }
    for(int i = stampVal[curKind]+1; i <= maxVal[curKind]+1; i++) {
        memset(vis, false, sizeof(vis));
        stampVal[curKind+1] = i;
        check(0, 0, curKind+1);
        int cnt = 0;
        while(vis[cnt]) {cnt++;}
        maxVal[curKind+1] = cnt-1;
        dfs(curKind+1);
    }
}
int main() {
    while(scanf("%d%d", &h, &K) != EOF && (h || K)) {
        stampVal[1] = 1;
        maxVal[1] = h;
        maxAns = -INF;
        dfs(1);
        for(int i = 1; i <= K; i++) {
            printf("%3d", ans[i]);
        }
        printf(" ->%3d\n", maxAns);
    }
    return 0;
}

以上题解转自

http://blog.csdn.net/shuangde800 , By D_Double

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值