题目
题意概要
对于给定的
n
n
n 和
a
1
,
a
2
,
a
3
,
…
,
a
k
a_1,a_2,a_3,\dots,a_k
a1,a2,a3,…,ak ,求一个非负整数数列
x
1
,
x
2
,
x
3
,
…
,
x
k
x_1,x_2,x_3,\dots,x_k
x1,x2,x3,…,xk ,满足
a i x i ∑ i = 1 k x i = n \frac{a_ix_i}{\sum_{i=1}^{k}x_i}=n ∑i=1kxiaixi=n
你只需要输出最小的 ∑ i = 1 k x i \sum_{i=1}^{k}x_i ∑i=1kxi 。
数据范围与提示
n
,
a
i
∈
[
0
,
1
0
3
]
n,a_i\in[0,10^3]
n,ai∈[0,103] ,而
k
≤
1
0
6
k\le 10^6
k≤106 。
思路
显然,将所有的 a i a_i ai 减去 n n n ,可以看出我们只需要 ∑ a i x i = 0 \sum a_ix_i=0 ∑aixi=0 即可。
k k k 很大是唬人的,不同的 a i a_i ai 才有意义。可以认为 k ≤ 1 0 3 k\le 10^3 k≤103 。
进行 d p \tt dp dp ,看出这是一个完全背包。背包的容量是多少呢?
如果你估计的不精确,利用 − 503 -503 −503 和 497 497 497 进行估算,那就是 503 × 497 = 249991 503\times 497=249991 503×497=249991 为绝对值上界。如果你仔细思考——对于当前的和,根据其正负性选择加上一个负数或正数,就只会在 [ − 2 × 1 0 3 , 2 × 1 0 3 ] [-2\times 10^3,2\times 10^3] [−2×103,2×103] 中摆动。优化了一百倍,非常恐怖,兄弟!
而每次的权值都是 1 1 1 ,利用 b f s \tt bfs bfs 优化转移的过程。然后就没了。
代码
这份代码的背包容量偏大,可以将 __M
改成
1001
1001
1001 。
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 1002;
const int __M = 499*500; // 偏移量
bool bucket[MaxN]; // 桶排
int a[MaxN], val, n;
int dp[__M<<1]; queue< int > q;
int bfs(){
for(int i=0; i<(__M<<1); ++i)
dp[i] = -1;
for(int i=1; i<=n; ++i){
dp[a[i]-val+__M] = 1;
q.push(a[i]-val+__M);
}
while(!q.empty()){
int t = q.front(); q.pop();
if(t == __M) return dp[t];
for(int i=1; i<=n; ++i){
int ne = t+a[i]-val;
if(ne < 0 || ne >= (__M<<1))
continue;
if(dp[ne] != -1) continue;
dp[ne] = dp[t]+1, q.push(ne);
}
}
return -1;
}
int main(){
val = readint(), n = readint();
for(int i=1; i<=n; ++i)
bucket[readint()] = true;
for(int i=n=0; i<MaxN; ++i)
if(bucket[i])
a[++ n] = i;
printf("%d\n",bfs());
return 0;
}