1 题目
给一组 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。 [规模最大的一个点的时限是3s]
小提示:因为14贴不出来,所以最高上限是13而不是15
1.1 输入
第 1 行: 两个整数,K 和 N。K(1 <= K <= 200)是可用的邮票总数。N(1 <= N <= 50)是邮票面值的数量。
第 2 行 … 文件末: N 个整数,每行 1 个,列出所有的 N 个邮票的面值,每张邮票的面值不超过 10000。
1.2 输出
第 1 行:一个整数,从 1 分开始连续的可用集合中不多于 K 张邮票贴出的邮资数。
1.3 样例
输入
5 2
1
3
输出
13
1.4 来源
USACO Training Section 3.1
石室联中 WOJ
2 分析
一看就是背包
2.1 70分(TLE)
分组背包,分K组,每组分别为各面值
记
f
i
f_i
fi表示面值
i
i
i可以表示
依次枚举第几组、想要表示的面值
i
i
i、各面值
w
j
w_j
wj(三层)
最后一个for找出ans
转移方程:
i
f
(
i
≥
w
j
)
f
i
=
f
i
∣
f
i
−
w
j
if(i\geq w_j)\ f_i = f_i | f_{i-w_j}
if(i≥wj) fi=fi∣fi−wj
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch;bool flag=0;
while(!isdigit(ch=getchar()))if(ch=='-')flag=1;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
const int MAXN = 55;
const int MAXV = 2000005;
typedef long long ll;
bool f[MAXV];
int n, V, K, w[MAXN];
int main(){
Read(K); Read(n);
for(register int i=1; i<=n; i++){
Read(w[i]);
V = max(V, w[i]);
}
f[0] = 1;
for(register int i=1; i<=K; i++)
for(register int j=V*i; j>=0; j--) for(register int k=1; k<=n; k++) if(j>=w[k]) f[j] = f[j-w[k]]|f[j];
for(register int i=1; i<=V*K+1; i++) if(!f[i]){
cout<<i-1<<endl;
return 0;
}
return 0;
}
2.2 正解
完全背包,记录每种拼出每种面值所需最少张数
最后一个for找出ans(最少张数大于K或为0)
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch;bool flag=0;
while(!isdigit(ch=getchar()))if(ch=='-')flag=1;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
const int MAXN = 55;
const int MAXV = 2000005;
typedef long long ll;
int f[MAXV];
int n, V, K, w[MAXN];
int main(){
Read(K); Read(n);
memset(f,127,sizeof(f));
for(register int i=1; i<=n; i++){
Read(w[i]);
V = max(V, w[i]);
}
V *= K;
f[0] = 0;
for(register int i=1; i<=n; i++)
for(register int j=w[i]; j<=V; j++)
f[j] = min(f[j],f[j-w[i]]+1);
for(register int i=1; i<=V+1; i++) if(f[i]>K){
cout<<i-1<<endl;
return 0;
}
return 0;
}
3 注意
3.1 初始化
f
0
=
0
f_0 = 0
f0=0(解法1中
f
0
=
t
r
u
e
f_0=true
f0=true)
解法2中
f
f
f数组的初始化(因为要取
m
i
n
min
min)
3.2 找答案
上限是 V + 1 V+1 V+1,不然如果在 V V V范围内都可以表示时,找不到答案(for循环不到 V + 1 V+1 V+1)