思路:dp[i] 表示使用状态i的硬币能买到的最多物品数,然后寻找比i状态多用一个硬币的 下一个状态 也就是状态 i&(1<<j) 进行转移。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
#define pb push_back
#define inf 0x3f3f3f3f
#define endl "\n"
#define d(a) cout<<#a<<a<<endl;
#define max(a, b) a > b ? a : b
const ll maxn=(1<<16)+20;
int k,n;
ll coin[20];
int s[100010];
int dp[maxn];
int find1(int l,int r,int p){
int now=s[l];
int res=1;
while (l<=r){
int mid=(l+r)>>1;
if (s[mid]-now<=p) res=mid,l=mid+1;
else r=mid-1;
}
return res;
}
int main(){
bool vis=false;
ll ans=0;
cin>>k>>n;
memset(dp,0,sizeof(dp));
memset(s,0,sizeof(s));
for (int i=1;i<=k;i++) {
cin>>coin[i];
}
for (int i=1;i<=n;i++) {
int mid;
cin>>mid;
s[i]=s[i-1]+mid;
}
for (int i=0;i<(1<<k);i++){
for (int j=0;j<k;j++){
if (i&(1<<j)) continue;
int mid;
mid=find1(dp[i],n,coin[j+1]);
dp[(i|(1<<j))]=max(dp[(i|(1<<j))],mid); //假设i状态用了x个硬币
//这个步骤就是寻找合法的使用x+1个硬币的状态,进行转移,硬币由少到多,是合法的,正确的。
}
if (dp[i]==n){
vis=true;
ll mid=0;
for (int j=0;j<k;j++){
if (i&(1<<j)) continue;
mid+=coin[j+1];
}
ans=max(ans,mid);
}
}
if (!vis) cout<<-1<<endl;
else cout<<ans<<endl;
}