题目
分析
一眼看到像是一道搜索,枚举可能的邮资,搜是否能用不多于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多加了一次。
}