传送门:牛客
题目描述:
约翰到商场购物,他的钱包里有K(1 <= K <= 16)个硬币,面值的范围是1..100,000,000。
约翰想按顺序买 N个物品(1 <= N <= 100,000),第i个物品需要花费c(i)块钱,(1 <= c(i) <= 10,000)。
在依次进行的购买N个物品的过程中,约翰可以随时停下来付款,每次付款只用一个硬币,支付购买的内容
是从上一次支付后开始到现在的这些所有物品(前提是该硬币足以支付这些物品的费用)。不幸的是,商场
的收银机坏了,如果约翰支付的硬币面值大于所需的费用,他不会得到任何找零。
请计算出在购买完N个物品后,约翰最多剩下多少钱。如果无法完成购买,输出-1
输入:
3 6
12
15
10
6
3
3
2
3
7
输出:
12
一道典型的状压dp题.
在刷完十几道状压dp题之后,对于此题我居然随便一写就写出来了,大喊一声"真爽",此题在洛谷上还是蓝题
废话不多说,下面是对此题的分析:
- 首先我们一看 k k k的范围只有16,大概率是用到状压dp了,在看一下题目,感觉是类似于贪心,但是贪心分析简单分析一下又是错的.那么对于这种大概率就是dp了
- 我们往状压dp的那个方向去想,我们用 d p [ S ] dp[S] dp[S]来记录我们用的硬币的状态为 S S S时(我们将硬币用了记为1,没用记为0),我们最大能买的物品的价值.注意此时我们记录的是物品的价值,这样更方便一点,然后因为我们的物品是顺序购买的,所以我们只要记录一下前缀和的话,配上二分就可以很容易的得到我们状态为S时买的物品的数量了
- 那么我们的转移也就呼之欲出了,当我们目前状态为S时我们可以枚举剩下的硬币,然后枚举每一个硬币,然后对于每一个新加的硬币,我们用二分找出现在我们能买的最多物品的位置即可
d p [ S ∣ ( 1 < < i ) ] = m a x ( d p [ S ∣ ( 1 < < i ) ] , s u m [ p − 1 ] ) ; dp[S|(1<<i)]=max(dp[S|(1<<i)],sum[p-1]); dp[S∣(1<<i)]=max(dp[S∣(1<<i)],sum[p−1]); - 对于我们最终的答案的话,判断一下每一种状态是不是能买到所有的物品了,如果能的话就记录一下,挑出最小值即可
下面是具体的代码部分:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int k,n;int dp[1<<17];int coin[17];int v[100020];int sum[100020];
int main() {
k=read();n=read();int ans=-1;
for(int i=0;i<k;i++) {
coin[i]=read();
}
for(int i=1;i<=n;i++) {
v[i]=read();sum[i]=sum[i-1]+v[i];
}
for(int S=0;S<1<<k;S++) {
for(int i=0;i<k;i++) {
if((S>>i)&1) continue;
int p=upper_bound(sum+1,sum+n+1,dp[S]+coin[i])-sum;
dp[S|(1<<i)]=max(dp[S|(1<<i)],sum[p-1]);
}
if(dp[S]>=sum[n]) {
int Sum=0;
for(int j=0;j<k;j++) {
if((S>>j)&1) continue;
Sum+=coin[j];
}
ans=max(ans,Sum);
}
}
if(ans==-1) cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
}