HDU5527 - Too Rich(贪心)

题意:

现在你有P元钱,有10种不同面值的硬币,每种硬币有一定的数量,求用尽量多的硬币凑出P元钱,如果凑不出输出“-1”。

 题解:

  • 一、贪心(反向)
    (可以看下:一位大佬十分详细的题解
    反向:求出所有硬币的总价值(sum)- 所求的价值(P),问题转化成用尽可能少的硬币去凑出这个差值(sum-P);
    贪心思想:从面值大的硬币开始,尽可能多地使用大面值硬币;
    算法核心:因为(20\rightarrow50)和(200\rightarrow500)不是整数倍的关系,所以不能直接贪心,那么就需要转化问题;当取偶数个50,可以用20拼凑出来,但取奇数个50时,用20是拼凑不出来的。

    我们可以枚举以下四种情况:
    (50和500都是偶数个,不先取50和500)
    (50为奇数个,500为偶数个,即我们先取一个50)
    (50为偶数个,500为奇数个,即我们先取一个500)
    (50为奇数个,500为奇数个,即我们先取一个50和一个500)
    之后对于50和500都成对地取,每次取50或500的时候就取偶数个。然后就可以用贪心了。
    #include <set>
    #include <map>
    #include <cmath>
    #include <stack>
    #include <queue>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstring>
    #include <sstream>
    #include <iomanip>
    #include <iostream>
    #include <algorithm>
    #define clr(str,x) memset(str,x,sizeof(str))
    #define FRER() freopen("in.txt","r",stdin);
    #define FREW() freopen("out.txt","w",stdout);
    #define INF 0x7fffffff
    #define maxn
    
    typedef long long int ll;
    using namespace std;
    int have[12],c[12],val[10]= {1,5,10,20,50,100,200,500,1000,2000};
    int consthave[12];
    int solve(int t)
    {
        int ans=0;
        for(int i=9; i>=0; i--)
        {
            if(i==4||i==7)
            {
                int temp = min(t/(2*val[i]),have[i]/2);
                ans += temp*2;
                t -= val[i] * 2 * temp;
            }
            else
            {
                int temp = min(t/val[i],have[i]);
                ans += temp;
                t -= val[i] * temp;
            }
            if(t==0)
                break;
        }
        if(t>0)
            return INF;
        else return ans;
    }
    int main()
    {
        //FRER()
        //FREW()
        int T,p;
        scanf("%d",&T);
        while(T--)
        {
            int sum=0,tol=0;
            scanf("%d",&p);
            for(int i=0; i<10; i++)
            {
                scanf("%d",&consthave[i]);
                sum+=consthave[i]*val[i];
                tol+=consthave[i];
            }
            sum-=p;
            if(sum<0)
            {
                printf("-1\n");
                continue;
            }
            int ans = INF;
            for(int i=0; i<2; i++)
            {
                for(int j=0; j<2; j++)
                {
                    memcpy(have,consthave,sizeof(consthave));
                    int t=sum;
                    if(i)
                    {
                        if(have[4])
                            t-=50,have[4]--;
                        else
                            continue;
                    }
                    if(j)
                    {
                        if(have[7])
                            t-=500,have[7]--;
                        else
                            continue;
                    }
                    if(t>=0)
                        ans=min(ans,solve(t)+i+j);
                }
            }
            if(ans==INF)
                printf("-1\n");
            else
                printf("%d\n",tol-ans);
        }
        return 0;
    }

     

  • 第二种贪心做法,有点类似搜索。
     

    #include <set>
    #include <map>
    #include <cmath>
    #include <stack>
    #include <queue>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstring>
    #include <sstream>
    #include <iomanip>
    #include <iostream>
    #include <algorithm>
    #define clr(str,x) memset(str,x,sizeof(str))
    #define FRER() freopen("in.txt","r",stdin);
    #define FREW() freopen("out.txt","w",stdout);
    #define INF 0x3f3f3f3f
    #define maxn
    
    typedef long long int ll;
    using namespace std;
    int have[10],val[]= {1,5,10,20,50,100,200,500,1000,2000};
    int consthave[10];
    int solve(int t)
    {
        int ans=0;
        for(int i=9; i>=0; i--)
        {
            if(i==4||i==7)
            {
                int temp = min(t/(val[i]*2),have[i]/2);
                ans += temp*2;
                t -= val[i] * 2 * temp;
            }
            else
            {
                int temp = min(t/val[i],have[i]);
                ans += temp;
                t -= val[i] * temp;
            }
            if(t==0)
                break;
        }
        if(t>0)
            return INF;
        else return ans;
    }
    
    int main()
    {
        //FRER()
        //FREW()
        int T,p;
        scanf("%d",&T);
        while(T--)
        {
            int sum=0,tol=0;
            scanf("%d",&p);
            for(int i=0; i<10; i++)
            {
                scanf("%d",&consthave[i]);
                sum+=consthave[i]*val[i];
                tol+=consthave[i];
            }
            sum-=p;
            if(sum<0)
            {
                printf("-1\n");
                continue;
            }
            int ans = INF;
            for(int i=0; i<2; i++)
            {
                for(int j=0; j<2; j++)
                {
                    memcpy(have,consthave,sizeof(consthave));
                    int t=sum;
                    if(i)
                    {
                        if(have[4])
                            t-=50,have[4]--;
                        else
                            continue;
                    }
                    if(j)
                    {
                        if(have[7])
                            t-=500,have[7]--;
                        else
                            continue;
                    }
                    if(t>=0)
                        ans=min(ans,solve(t)+i+j);
                }
            }
            if(ans==INF)
                printf("-1\n");
            else
                printf("%d\n",tol-ans);
        }
        return 0;
    }

     

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值