POJ 3040 Allowance——经典贪心算法题目

题目链接

题目大意:

约翰要给他的牛贝西发工资,每天不得低于C元,约翰有n种面值的钱币,第i种的面值为v_i,数量有b_i。问这些钱最多给贝西发多少天的工资。注意,每种面值的金钱都是下一种的面值的倍数。
例子
Sample Input

3 6
10 1
1 100
5 120
Sample Output

111

题目分析:

  1. 典型的贪心算法,每一次发工资,都要考虑当前所能实现的最优策略。
  2. 对coin数组按照面值大小从小到大排序,然后将数值大于等于C的先提前计算。
  3. 为了能最大程度实现每次发的工资都尽可能贴近C,我们需要先从大到小进行分配,分配过程中,费用不能超过C;然后,如果费用不等于C,也就是仍然需要面值小的进行补偿,就需要从小到大进行分析,这时的费用可以超过C,因为是从小到大,我们可以将小面值用到极致,也就是能用的都用了。
  4. 贪心过程需要used数组记录每一个面值所使用的数目,这样就可以找到最优组合可以实现多少次,也就是coin[i].num/used[i]中的最小值。
  5. 当搭配完毕后,若出现费用仍然大于零的情况,就说明当前的钱不够分工资了,退出循环。
  6. 这道题题干说后一个数值是前一个的倍数这一特点,在这道题里并没有找到运用的地方。

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<limits.h>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<set>
#include<vector>
#include<queue>
using namespace std;
struct Coin {
	int value;
	int num;
	Coin() {}
	Coin(int x,int y):value(x),num(y) {}
	bool operator< (const Coin& other)const {
		return value<other.value;
	}
} coin[20];
int used[20];
int main() {
	int n,c;
	while(scanf("%d%d",&n,&c)!=EOF) {
		for(int i=0; i<n; i++) {
			scanf("%d%d",&coin[i].value,&coin[i].num);
		}
		int res=0;
		sort(coin,coin+n);
		for(int i=n-1; i>=0; i++) {
			if(coin[i].value>=c) {
				res+=coin[i].num;
				coin[i].num=0;
			} else {
				break;
			}
		}
		while(true) {
			memset(used,0,sizeof(used));
			int cou=c;
			//从大到小 先挑面值大的,总值不能超过c
			for(int i=n-1; i>=0; i--) {
				if(coin[i].num!=0) {
					int temp=cou/coin[i].value;
					temp=min(temp,coin[i].num);
					used[i]=temp;
					cou-=temp*coin[i].value;
					if(cou==0) {
						break;
					}
				}
			}
			//从小到大 补足剩余的钱 总值可以超过c
			if(cou>0) {
				for(int i=0; i<n; i++) {
					if(coin[i].num>0) {
						while(used[i]<coin[i].num) {
							cou-=coin[i].value;
							used[i]++;
							if(cou<=0) {
								break;
							}
						}
						if(cou<=0) {
							break;
						}
					}
				}
			}
			if(cou>0) {
				break;
			}

			int temp=INT_MAX;
			for(int i=0; i<n; i++) {
				if(used[i]!=0) {
					temp=min(temp,coin[i].num/used[i]);
				}
			}
			res+=temp;
			for(int i=0;i<n;i++){
				if(used[i]!=0){
					coin[i].num-=used[i]*temp;
				} 
			}
		}
		printf("%d\n",res);
	}
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值