4
≤
k
≤
16
4 \leq k \leq 16
4≤k≤16 于是我们考虑状压
D
P
DP
DP
设
f
[
i
]
f[i]
f[i]表示当前状态为
i
i
i时所能选买到的最大的商品的位置
设状态为
i
i
i,当前选到的硬币为
j
j
j
显然有状态转移方程
f
[
i
]
=
m
a
x
(
f
[
i
]
,
g
e
t
(
s
u
m
[
f
[
i
x
o
r
(
1
<
<
j
)
]
]
+
c
[
j
]
)
)
f[i]=max(f[i], get(sum[\ f[i\ \ xor \ \ (1<<j)] \ \ ]+c[j]))
f[i]=max(f[i],get(sum[f[ixor(1<<j)]]+c[j]))
s
u
m
sum
sum数组表示前缀和
g
e
t
(
x
)
get(x)
get(x)求出的是值为
x
x
x的货币从一开始所能买到的最大的商品的位置
因为前缀和具有单调性所以我们可以二分查找
对于每种状态只有
f
[
i
]
=
n
f[i]=n
f[i]=n时可以统计答案
code
#include<bits/stdc++.h>usingnamespace std;typedeflonglong LL;constint maxn =20;constint maxk =1<<17;constint maxm =1e5+100;template<typename T>inlinevoidread(T &s){
s =0;
T w =1, ch =getchar();while(!isdigit(ch)){if(ch =='-') w =-1; ch =getchar();}while(isdigit(ch)){ s =(s <<1)+(s <<3)+(ch ^48); ch =getchar();}
s *= w;}
LL ans, n, k;
LL sum[maxm], f[maxk], c[maxn];inlineintget(LL x){int l =1, r = n, pos =0;while(l <= r){int mid =((l + r)>>1);if(sum[mid]<= x)
l = mid +1, pos = mid;else r = mid -1;}return pos;}intmain(){read(k),read(n);for(int i =1; i <= k;++i)read(c[i]);for(int i =1; i <= n;++i)read(sum[i]), sum[i]+= sum[i -1];for(int s =0; s <(1<<k);++s){for(int i =1; i <= k;++i){if(!(s &(1<<(i-1))))continue;
LL temp = f[(s ^(1<<(i-1)))];
LL pos =get(sum[temp]+ c[i]);
f[s]=max(f[s], pos);}}
ans =-1;for(int s =0; s <(1<<k);++s){if(f[s]== n){
LL cnt =0;for(int j =1; j <= k;++j){if(!(s &(1<<(j-1)))) cnt += c[j];}
ans =max(ans, cnt);}}printf("%lld\n", ans);return0;}