[USACO13NOV]没有找零No Change 题解
题目描述
约翰到商场购物,他的钱包里有K(1 <= K <= 16)个硬币,面值的范围是1..100,000,000。
约翰想按顺序买 N个物品(1 <= N <= 100,000),第i个物品需要花费c(i)块钱,(1 <= c(i) <= 10,000)。
在依次进行的购买N个物品的过程中,约翰可以随时停下来付款,每次付款只用一个硬币,支付购买的内容是从上一次支付后开始到现在的这些所有物品(前提是该硬币足以支付这些物品的费用)。不幸的是,商场的收银机坏了,如果约翰支付的硬币面值大于所需的费用,他不会得到任何找零。
请计算出在购买完N个物品后,约翰最多剩下多少钱。如果无法完成购买,输出-1
输入输出格式
输入格式:
-
Line 1: Two integers, K and N.
-
Lines 2..1+K: Each line contains the amount of money of one of FJ's coins.
-
Lines 2+K..1+N+K: These N lines contain the costs of FJ's intended purchases.
输出格式:
- Line 1: The maximum amount of money FJ can end up with, or -1 if FJ cannot complete all of his purchases.
输入输出样例
输入样例#1:
3 6
12
15
10
6
3
3
2
3
7
输出样例#1: 复制
12
说明
FJ has 3 coins of values 12, 15, and 10. He must make purchases in sequence of value 6, 3, 3, 2, 3, and 7.
FJ spends his 10-unit coin on the first two purchases, then the 15-unit coin on the remaining purchases. This leaves him with the 12-unit coin.
题目大意:
给出k个硬币,n个商品,可以调整使用硬币的顺序,依次购买商品,每次不会找零,求最后剩下的最大钱数,如不能买全,输出-1
因为\(k\leq16\),考虑状压
设\(dp_{sta}\)表示状态为\(sta\)时(二进制,表示哪些硬币使用了,哪些没使用),从一号物品开始能购买的物品数量
显然有
当使用当前状态中未使用的一个硬币时
\(dp[new\_sta]=max(dp[new\_sta],k)\)
其中\(new\_sta\)是转移后的新状态
用\(sum_k\)表示前\(i\)个物品的总售价
则k为\(sum_k\)能够支付起是的最大物品数量(最优性)
二分一下即可求出k
代码:
#include<bits/stdc++.h>
#define ll long long
#define INF 2147483647
#define mem(i,j) memset(i,j,sizeof(i))
#define F(i,j,n) for(register int i=j;i<=n;i++)
#define lowbit(i) i&(-i)
using namespace std;
int k,n,c[20],p[100010],dp[1100000],sum[100010];
inline int read(){
int datta=0;char chchc=getchar();bool okoko=0;
while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
return okoko?-datta:datta;
}
int main(){
k=read();n=read();
F(i,1,k)
c[i]=read(),c[0]+=c[i];
F(i,1,n)
p[i]=read(),sum[i]=sum[i-1]+p[i];
mem(dp,-1);
dp[0]=0;
int ans=2147483647;
F(sta,0,(1<<k)-1){
if(dp[sta]==-1)
continue;
if(dp[sta]==n){
int _ans=0;
F(i,1,k)
if(sta&(1<<(i-1)))
_ans+=c[i];
ans=min(ans,_ans);
continue;
}
F(i,1,k){
if(sta&(1<<(i-1)))
continue;
int l=dp[sta]+1,r=n,mid,_ans=-1;
while(l<=r){
mid=(l+r)>>1;
if(sum[mid]<=sum[dp[sta]]+c[i])
l=mid+1,_ans=mid;
else
r=mid-1;
}
if(_ans==-1)
continue;
dp[sta|(1<<(i-1))]=max(dp[sta|(1<<(i-1))],_ans);
}
}
printf("%d\n",ans==2147483647?-1:c[0]-ans);
return 0;
}