Luogu P2725 邮票 Stamps

题目

Luogu P2725 邮票 Stamps

分析

一眼看到像是一道搜索,枚举可能的邮资,搜是否能用不多于k张邮票拼凑出当前值来。分析一下复杂度,邮资的最大值是所有可贴的地方全贴满最大面值的邮票,也就是 max(aik) m a x ( a i ∗ k ) =2e6,每个邮资搜索k层,每层n个状态,那么每一次就要搜索 nk n k 次。那么总操作数大概是 2e6(50200) 2 e 6 ∗ ( 50 200 ) ……
换个思路。有点像是完全背包问题?于是又想到设f[i]为选择邮资为i的各种邮票所需的数量。通俗的来说,就是拼凑出邮资i来需要的邮票数。刷表法是个好东西!当枚举到f[i]时,f[i+a[j]](j为某一面值邮票,a[j]为j邮票的价值)的值就可以确定,即比f[i]多用一张面值为a[j]的邮票。边界条件是,邮资为0时不需要邮票,即f[0]=0。很容易写出方程:
f[i+a[j]]=min f [ i + a [ j ] ] = m i n { f[i]+1 f [ i ] + 1 };
取min是为了防止这种情况:本来有一种方案可以在k张之内拼凑出,但在之后的转移时发现需要更多张,答案就会被覆盖,而如果此时恰好超过了k张,就变成了无法拼凑的答案了。
复杂度就是枚举邮资,枚举所使用的邮票种类,即O( max(aik)n m a x ( a i ∗ k ) ∗ n ),大约是1e8,有些惊险。但面值最大才是10000,所以实际上比1e8要小不少,因此可过。

代码

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=10002;
int a[52],f[maxn];
int main()
{
    int n,k,num=0;
    scanf("%d%d",&k,&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),num=max(num,a[i]);
    num*=k;
    for(int i=1;i<=num;i++)//记得要赋初值,不然取min操作就废了,但f[0]=0是边界,不赋为极小值。
        f[i]=2147483647;
    for(int i=0;i<=num;i++)
        for(int j=1;j<=n;j++)
                f[i+a[j]]=min(f[i+a[j]],f[i]+1);
    int i=1;
    while(f[i]<=k&&f[i]>0)  i++;//能够在符合条件的情况下拼凑出来的话,就判断下一邮资是否可以。
    printf("%d",i-1);//输出i-1是因为while多加了一次。
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值