UVa 165 - Stamps

这个题题目大意就比较难理解。题意:给定 h(贴邮票位置数),k(允许使用的邮票面值数) (h+k<=9)  求n(h,k) (即用k种面值的邮票在最大h个位置拼成连续面值的最大值) 。

样例:h=3 k=2。若两种面值为1,3  则可以拼出 1,2,3,4,5 ,6 ,7 ,9 连续的最大和只到7。

因此输出:

 1 3 -> 7
思路:数据量不大,可以一个一个数的往后推,首先,面值为1的邮票是必须选的,否则起码面值和“1”就凑不出来了。从1着手,其下一个数的取值范围必定在当前面值凑出数的(最小值+1)与(最大值+1)之间,比如:h为3,k为3,当前为1 3,则下一个数的范围为:4~8之间,下一个数的取值小了重复,而大了,则必定会出现断点,比如上例中如果下一个数是9,因为8必然凑不出来,所以9就是无效数据。以此回溯枚举所有位置面值的和,再判定连续的最大值便是所求的结果。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>

using namespace std;
int kval[11], save[11];
bool visit[190]; // 180时还RE,貌似最大面值和能达到180+
int h, k, _max;

void check(int n, int cur, int sum) // 将当前所具有的面值 和的所有情况都枚举出来并利用visit数组进行标记
{
    if(cur == h)
    {
        visit[sum] = true;
        return ;
    }
    visit[sum] = true;
    for(int i=0; i <= n; i++)
        check(n, cur+1, sum+kval[i]);
}
void dfs(int cur, int num) // 依次往下推导下一个面值
{
    if(cur > k)
    {
        if(num-1 > _max)
        {
            _max = num-1;
            memcpy(save, kval, sizeof(kval));
        }
        return ;
    }
    for(int i=kval[cur-1]+1; i<=num; i++)
    {
        kval[cur] = i;
        memset(visit, false, sizeof(visit));
        check(cur, 0, 0);
        int j = kval[cur-1];//从上一层的面值和的数开始判断,直到遇到第一个false跳出
        while(visit[++j])
            ;
        dfs(cur+1, j);
    }
}
int main()
{
#ifdef test
    freopen("sample.txt", "r", stdin);
#endif
    kval[0] = 0;
    kval[1] = 1;
    while(scanf("%d%d", &h, &k))
    {
        if(!h && !k)
            break;
        _max = 0;
        dfs(2, h+1);
        for(int i=1; i<=k; i++)
            printf("%3d", save[i]);
        printf(" ->");
        printf("%3d\n", _max);
    }
    return 0;
}


你可能感兴趣的:(UVa 165 - Stamps)