当前位置: 代码迷 >> 综合 >> Luogu P2725 邮票 Stamps
  详细解决方案

Luogu P2725 邮票 Stamps

热度:44   发布时间:2024-01-12 01:27:04.0

题目

Luogu P2725 邮票 Stamps

分析

一眼看到像是一道搜索,枚举可能的邮资,搜是否能用不多于k张邮票拼凑出当前值来。分析一下复杂度,邮资的最大值是所有可贴的地方全贴满最大面值的邮票,也就是 max(ai?k) 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(ai?k)?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多加了一次。
}