给面额c1,c5,c10,c20,c50,c100,c200,c500,c1000,c2000硬币若干,让你用最多的钱币凑出恰好p元,可能无解
这道题是2015长春区域赛的金牌题。。场外头脑风暴一个晚上+一个中午,推翻大量算法的路过
一般的贪心法是尽可能多取小面额硬币,但是这样可能会被p=70 (20,20,20,50) 卡掉,因为贪心取20结果答案变成无解
原因是20凑不出50.
所以我们考虑50取奇数还是偶数的情况
这样的话
50就变成(强制取1张50)+若干张100了 然后就符合贪心情况了
5,500 同理
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <cstdlib>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
#define For(i,n) for(int i=1;i<=n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,n) for(int i=k;i<=n;i++)
#define MAXN (10+10)
int n,c[MAXN];
int c2[MAXN];
const int a[11]={0,1,5,10,20,50,100,200,500,1000,2000};
ll calc(ll x) {
ll tmp=0;
ForD(i,10) {
ll p=min(x/a[i],(ll)c2[i]);
if (i==2||i==5||i==8) p=p/2*2;
tmp+=p;
x-=p*a[i];
}
if (x==0) return tmp;
return 1000000000;
}
int main() {
int T;
cin>>T;
while(T--) {
ll p,c[20];
cin>>p;
int s=0;
ll tot=0;
For(i,10) cin>>c[i],s+=c[i],tot+=c[i]*a[i];
//For(i,10) cout<<c[i]<<' ';cout<<endl;
ll p2=tot-p;
if (p2<0) {
cout<<"-1"<<endl;
continue;
}
ll ans=1000000000;
for(int i=0;i<8;++i){
p=p2;
int t=0;
For(j,10) c2[j]=c[j];
if (i&1 && p>=5 && c2[2]>0 ) {
p-=5; c2[2]--; ++t;
}
if (i&2 && p>=50 && c2[5]>0 ) {
p-=50; c2[5]--; ++t;
}
if (i&4 && p>=500 && c2[8]>0 ) {
p-=500; c2[8]--; ++t;
}
ll ans2=calc(p);
if (ans2==1000000000) continue;
ans=min(ans,ans2+t);
}
if (ans==1000000000) cout<<"-1"<<endl;
else cout<<s-ans<<endl;
}
return 0;
}