HDU 5527(Too Rich-贪心)

给面额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;
}

你可能感兴趣的:(头脑风暴)