递推+贪心——金币

描述:
有n个金币,每个金币的面值都是2的幂。现在给出若干个查询,每次查询一个数值最少需要多少个金币。如果不能由这些金币组合出,那么输出-1。
输入:
第一行两个整数n和q,分别表示金币的数量和查询的数量。(1<=n,q<=200000)
第二行有n个正整数,表示金币的面值,这些数保证是2的幂,且不超过2000000000。
接下来有q行,表示q个查询。每行一个整数,不超过1000000000。
输出:
q行,对于每个询问的回答。
样例输入:

5 4
2 4 8 2 4
8
5
14
10

样例输出:

1
-1
3
2

这道题目是一道递推+贪心的综合题目,解题思路由下:
1:由于金币的面值不超过int范围且都为2的整数幂,所以可以用递推方式求出各种面值的钱币,存在数组1中(注意,包含2的0次方)。
:2:输入实际存在的钱币数n,并统计这些钱币在31种面值的钱币中存在多少枚,存在数组2中。
通过以上步骤我们拿到了数组1(31种钱币的面值)和数组2(实际存在的面值的钱币数量)。
3:既然题目要求最少的组合数,那么我们可以1~q输入一个总数x之后,从最大面值的钱币开始找(30~0),如果(钱币存在且面值小于输入的这个总数x),则进行相应操作。
代码如下:

//这道题数据量较大,尽量使用格式化输入输出。
#include<cstdio>
#include<iostream>
using namespace std;
int n,q,a[200005],x,c[35]={1},d[35],tot;
int main()
{
    for(int i=1;i<=31;i++)
    {
        c[i]=c[i-1]*2;
    }//求出各种面额的钱币。
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        for(int j=0;j<=31;j++)
        {
            if(c[j]==a[i])
            {
                d[j]++;//输入时统计各种面额钱币的数量,以节省时间。
                break;//找到就退出循环。
            }
        }   
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%d",&x);
        tot=0;//将个数清0。
        for(int j=31;j>=0;j--)
        {
            int t;//定义最多钱币个数。
            if(c[j]<=x&&d[j]>0)
            {
                t=x/c[j];
                if(t>d[j])//如果最多钱币个数>实际钱币个数,减去实际钱币数*面额。
                {
                    x-=d[j]*c[j];
                    tot+=d[j];
                }
                else//否则,减去最多钱币个数*面额。
                {
                    x-=t*c[j];
                    tot+=t;
                }
                if(x==0)//如果分完了,退出循环。
                {
                    break;
                }
            }
        }
        if(x==0)//如果分完,输出最少钱币数。
        {
            printf("%d\n",tot);
        }
        else//分不完,输出-1。
        {
            printf("-1\n");
        }
    }
    return 0;
}

小结:这道题其实找到了正确思维,其实不难,在平常做题要足够仔细,才能万无一失。(例如本题a[i]的范围)

猜你喜欢

转载自blog.csdn.net/qq_42990749/article/details/81749548
今日推荐