递推+贪心——金币

一道关于计算金币组合的编程题,要求使用递推和贪心策略。给定n个面值为2的幂的金币,以及q个查询,需要找出每个查询值所需的最小金币数。首先用递推生成所有可能的面值,然后根据贪心策略从最大面值开始匹配。代码实现中需要注意细节,以确保正确性。
摘要由CSDN通过智能技术生成

描述:
有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]的范围)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值