[Usaco2013 Nov]No Change

Description
Farmer John is at the market to purchase supplies for his farm. He has in his pocket K coins (1 <= K <= 16), each with value in the range 1..100,000,000. FJ would like to make a sequence of N purchases (1 <= N <= 100,000), where the ith purchase costs c(i) units of money (1 <= c(i) <= 10,000). As he makes this sequence of purchases, he can periodically stop and pay, with a single coin, for all the purchases made since his last payment (of course, the single coin he uses must be large enough to pay for all of these). Unfortunately, the vendors at the market are completely out of change, so whenever FJ uses a coin that is larger than the amount of money he owes, he sadly receives no changes in return! Please compute the maximum amount of money FJ can end up with after making his N purchases in sequence. Output -1 if it is impossible for FJ to make all of his purchases.

K个硬币,要买N个物品。
给定买的顺序,即按顺序必须是一路买过去,当选定买的东西物品序列后,付出钱后,货主是不会找零钱的。
现希望买完所需要的东西后,留下的钱越多越好,如果不能完成购买任务,输出-1

Input

  • 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.

Output

  • Line 1: The maximum amount of money FJ can end up with, or -1 if FJ cannot complete all of his purchases.

Sample Input
3 6
12
15
10
6
3
3
2
3
7

Sample Output
12


按顺序来这一点保证了我们能够对一枚硬币买到的东西一定是一段区间,那么我们就可以找到一个起始点来二分查找结束点。设F[sta]表示使用的硬币状态为sta,所能买到的最多的物品个数,每次转移时枚举一个没用过的硬币,从F[sta]开始向后买尽可能多的东西,结束点可以使用二分来找到。

(F[sta|(1<<(i-1))]=max(F[sta|(1<<(i-1))],find(F[sta],coin[i]));)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
	int x=0,f=1;char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')    f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x>=10)	print(x/10);
	putchar(x%10+'0');
}
const int N=1e5;
int val[N+10],sum[N+10];
int f[(1<<17)+10],A[20];
int limit;
int find(int x,int k){
	k+=sum[x];
	int l=x+1,r=limit;
	while (l<=r){
		int mid=(l+r)>>1;
		if (k<sum[mid])	r=mid-1;
		else	l=mid+1;
	}
	return l-1;
}
int main(){
	int k=read(),n=read(),Ans=-inf; limit=n;
	for (int i=1;i<=k;i++)	A[i]=read();
	for (int i=1;i<=n;i++)	sum[i]=sum[i-1]+(val[i]=read());
	memset(f,255,sizeof(f));
	f[0]=0;
	for (int sta=0;sta<1<<k;sta++){
		if (f[sta]==-1)	continue;
		if (f[sta]==n){
			int res=0;
			for (int i=1;i<=k;i++)	if (!(sta&(1<<(i-1))))	res+=A[i];
			Ans=max(Ans,res);
		}
		for (int i=1;i<=k;i++){
			if (sta&(1<<(i-1)))	continue;
			f[sta|(1<<(i-1))]=max(f[sta|(1<<(i-1))],find(f[sta],A[i]));
		}
	}
	printf("%d
",Ans==-inf?-1:Ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Wolfycz/p/9656718.html