【usaco/codevs2033/codevs1047/NOIP1999TG】 邮票问题浅谈

245 篇文章 0 订阅
72 篇文章 0 订阅

1、邮票  usaco/codevs2033黄金Gold

题目描述 Description

已知一个 N枚邮票的面值集合(如,{1分,3})和一个上限 K ——表示信封上能够贴 K张邮票。计算从 1 M的最大连续可贴出的邮资。

例如,假设有 1分和 3分的邮票;你最多可以贴 5张邮票。很容易贴出 1 5分的邮资(用 1分邮票贴就行了),接下来的邮资也不难:

6 = 3 + 3

7 = 3 + 3 + 1

8 = 3 + 3 + 1 + 1

9 = 3 + 3 + 3

10 = 3 + 3 + 3 + 1

11 = 3 + 3 + 3 + 1 + 1

12 = 3 + 3 + 3 + 3

13 = 3 + 3 + 3 + 3 + 1

然而,使用 5 1分或者 3 分的邮票根本不可能贴出 14分的邮资。因此,对于这两种邮票的集合和上限 K=5,答案是 M=13


小提示:因为14贴不出来,所以最高上限是13而不是15

输入描述 InputDescription

1行:两个整数,K NK1 <= K <= 200)是可用的邮票总数。N1 <= N <= 50)是邮票面值的数量。

2 ..文件末: N 个整数,每行 15个,列出所有的 N个邮票的面值,每张邮票的面值不超过 10000

输出描述 OutputDescription

1行:一个整数,从 1分开始连续的可用集合中不多于 K张邮票贴出的邮资数。

样例输入 Sample Input

5 2

1 3

样例输出 Sample Output

13

数据范围及提示 Data Size& Hint

 

【数据分析】【空间复杂度可以承受】

最多贴k<=200张,每张面值不超过10^4,所以2*10^6可以用一个一维数组存储。

 

【算法分析】【深搜TLE用dp】

简单的暴搜时间复杂度会达到50^20,是我们难以承受的;

我们可以用递推来做:

阶段:以能够成的面值为每个阶段。如样例中一共有13个阶段;

状态:f[i]表示构成面值i所需的最少邮票个数;

决策:如果当前能组成的面值i减去某一张邮票的面值再+1<f[i]的话,就说明构成面值i的邮票个数还可以更少,就是说用当前的那一张邮票来组成面值i的话,是当前的最优解,,用状态转移方程来表示:f[i]=min(f[i],f[i-a[j]]+1);

这就是dp的思路,时间复杂度O(nk);

 

【代码】

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,i,j,a[100000],f[100000];
int main()
{
        scanf("%d%d",&n,&k);
        for(i=1;i<=n;++i)
          scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        f[0]=0;
        i=0;
        while(f[i]<=k)
        {
               i++;
               f[i]=2147483647;
               for(j=1;j<=n;++j)
                 if (a[j]<=i&&f[i-a[j]]+1<f[i])
                   f[i]=f[i-a[j]]+1;
        }
        printf("%d",i-1);
        return0;
}</span>


2、邮票面值设计 NOIP1999TG/codevs1047钻石Diamond

题目描述 Description

 

给定一个信封,最多只允许粘贴N张邮票,计算在给定KN+K≤40)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1MAX之间的每一个邮资值都能得到。

   

    例如,N=3K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。

输入描述 InputDescription

NK

输出描述 OutputDescription

每种邮票的面值,连续最大能到的面值数。数据保证答案唯一。

样例输入 Sample Input

3 2

样例输出 Sample Output

1 3

MAX=7

【数据分析】【空间复杂度可以接受】

 N+K<=40;4*10^6(学姐说最高可达3*10^7)

【算法分析】【深搜+dp】

 N+K<=40; 

 与上一题的区别在于还需枚举邮票的面值,枚举的过程可以用深搜来实现;

首先明确:

第一个面值一定是1,假如说最多贴n张,要保证连续,那么第二张面值最大是1*n+1,最小是1+1;也就是说,如果已经选定了dep-1个面值,现在要选第dep个面值,那么第dep个面值的范围只能是a[dep-1]+1到a[dep-1]*n+1,这也就规定了枚举的范围,那么用深搜就好做很多了;

我们可以用深搜生成很多面值的组合,然后再用类似前一题的dp来求出最大的连续面值,即是这道题的答案。

 

【代码】

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[4000005],a[50],ans[50];
int n,k,maxn,i;
 
void work()//和上面的dp过程基本相同,ans和maxn是最终的答案
{
        inti,j;
        f[0]=0;
        i=0;
        while(f[i]<=n)
        {
               i++;
               f[i]=2147483647;
               for(j=1;j<=n;++j)
                 if (i>=a[j]&&f[i-a[j]]+1<f[i])
                   f[i]=f[i-a[j]]+1;
        }
        if(i-1>maxn)
        {
               maxn=i-1;
               for(j=1;j<=k;++j)
                 ans[j]=a[j];
        }
        return;
}
 
void dfs(int dep)
{
        intr;
        if(dep==k+1)
        {
               work();//如果已经生成好了一种可能的组合,就进行一次dp
               return;
        }
        for(r=a[dep-1]+1;r<=a[dep-1]*n+1;++r)//这就是前面说的枚举的范围
        {
               a[dep]=r;//a是记录数组
               dfs(dep+1);
        }
        return;
}
 
int main()
{
        freopen("a5.in","r",stdin);
        freopen("a5.out","r",stdout);
        scanf("%d%d",&n,&k);
        maxn=0;
        dfs(1);//首先深搜生成所有可能的面值组合
        for(i=1;i<=k;++i)
          printf("%d ",ans[i]);
        printf("\n");
        printf("MAX=%d",maxn);
        return0;
}</span>

邮票问题是经典问题,在学递推和搜索的时候都出现了。通过这道题可以更深刻的理解搜索与dp各自的原理以及他们的结合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值