[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;
}
posted @ 2019-02-17 23:54  hzf29721  阅读(248)  评论(0编辑  收藏  举报