贪心专题讲解

概述:

       贪心算法是指,在对问题求解时,总是以当前情况为基础作最优选择,而不考虑各种可能的整体情况,它所做出的仅仅是在某种意义上的局部最优解,省去了为找最优解要穷尽所有可能而必须耗费的大量时间,类似数学归纳法,无后效性,在运行过程中没有回溯过程,每一步都是当前的最佳选择。

        难点是如何贪心和证明贪心的正确性,即如何用一个小规模的解构造更大规模的解,比赛过程中需要胆大心细地归纳、分析。

贪心的缺点:

  1. 可能得不到最优解
  2. 可能算不出答案

用贪心法求解需要满足以下特征:

  1. 最优子结构性质(当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质)
  2. 贪心选择性质

贪心常见问题

  1. 活动安排问题(区间调度问题)
  2. 区间覆盖问题
  3. 最优装载问题
  4. 多机调度问题

问题一

"Yakexi, this is the best age!" Dong MW works hard and get high pay, he has many 1 Jiao and 5 Jiao banknotes(纸币), some day he went to a bank and changes part of his money into 1 Yuan, 5 Yuan, 10 Yuan.(1 Yuan = 10 Jiao)
"Thanks to the best age, I can buy many things!" Now Dong MW has a book to buy, it costs P Jiao. He wonders how many banknotes at least,and how many banknotes at most he can use to buy this nice book. Dong MW is a bit strange, he doesn't like to get the change, that is, he will give the bookseller exactly P Jiao.

Input

T(T<=100) in the first line, indicating the case number.
T lines with 6 integers each:
P a1 a5 a10 a50 a100
ai means number of i-Jiao banknotes.
All integers are smaller than 1000000.

Output

Two integers A,B for each case, A is the fewest number of banknotes to buy the book exactly, and B is the largest number to buy exactly.If Dong MW can't buy the book with no change, output "-1 -1".

Sample Input

3
33 6 6 6 6 6
10 10 10 10 10 10
11 0 1 20 20 20

Sample Output

6 9
1 10
-1 -1

大致题意: 给定总钱数,一元,五元,十元,五十元,一百元的硬币的个数,求出使用最少和最多的硬币数。

思路:这是一个经典的贪心硬币问题,只需从大面值硬币开始选择,一直到最小面值的硬币,最后统计硬币的总数量,即为硬币的最少使用量。

硬币的最大使用量的求法则有些特殊,先求出总钱数与所有硬币总值的差值,再求出这部分差值使用硬币的最少数量,最后用总硬币数量减去差值使用硬币的最少数量,所得结果即为硬币的最多使用量。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<string.h>
#include<algorithm>
using namespace std;

int main() {
	int t;
	cin>>t;
	while(t--) {
		int mony,mony1=0;
		int a,b,c,d,e;
		int num=0;
		int num1=0;
		cin>>mony>>a>>b>>c>>d>>e;
		mony1=a*1+b*5+c*10+d*50+e*100-mony;
		int q=0;
		//最少
		if(mony>=100) {
			q=mony/100;
			if(q>=e) {
				q=e;
			}
			mony-=100*q;
			num+=q;
		}
		if(mony>=50) {
			q=mony/50;
			if(q>=d) {
				q=d;
			}
			mony-=50*q;
			num+=q;
		}
		if(mony>=10) {
			q=mony/10;
			if(q>=c) {
				q=c;
			}
			mony-=10*q;
			num+=q;
		}
		if(mony>=5) {
			q=mony/5;
			if(q>=b) {
				q=b;
			}
			mony-=5*q;
			num+=q;
		}
		if(mony>=1) {
			q=mony/1;
			if(q>=a) {
				q=a; 
			}
			mony-=1*q;
			num+=q;
		}
		if(mony==0) {
			cout<<num<<" ";
		} else {
			cout<<-1<<" ";
		}

		//最大
		if(mony1>=100) {
			q=mony1/100;
			if(q>=e) {
				q=e;
			}
			mony1-=100*q;
			num1+=q;
		}
		if(mony1>=50) {
			q=mony1/50;
			if(q>=d) {
				q=d;
			}
			mony1-=50*q;
			num1+=q;
		}
		if(mony1>=10) {
			q=mony1/10;
			if(q>=c) {
				q=c;
			}
			mony1-=10*q;
			num1+=q;
		}
		if(mony1>=5) {
			q=mony1/5;
			if(q>=b) {
				q=b;
			}
			mony1-=5*q;
			num1+=q;
		}
		if(mony1>=1) {
			q=mony1/1;
			if(q>=a) {
				q=a; 
			}
			mony1-=1*q;
			num1+=q;
		}
		if(mony1==0) {
			cout<<a+b+c+d+e-num1;
		} else {
			cout<<-1;
		}
		cout<<endl;
	}
	return 0;
}

问题二

电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。

Input

多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。

Output

对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。

Sample Input

1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0

Sample Output

-45
32

 思路:突破口:将菜的价格从小到大排序,对前n-1个物品进行DP,找出消费金额不超过m-5的方案中最大的消费金额sum,在此基础上,m - (sum+price[n]) 即为卡内最小余额。因此,我们DP应在余额为0~m-5之内找出最优的解。

这道题中需要用到DP,即动态规划,详细讲解参考b站《动态规划 硬币问题》

动态规划 硬币问题_哔哩哔哩_bilibili

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<string.h>
#include<algorithm>
using namespace std;

int num[1005];
int dp[1005];
int main() {
	int n;
	while(cin>>n) {
		if(n==0) {
			break;
		}
		for(int i=0; i<n; i++) {
			cin>>num[i];
		}
		memset(dp,0,sizeof(dp)); 
		sort(num+0,num+n);
		int mony;
		cin>>mony;
		if(mony<5) {
			cout<<mony<<endl;
		}else{
			for(int i=0;i<n-1;i++){
				for(int j=mony-5;j>=num[i];j--){
					//dp[j]=max(dp[j],dp[j-num[i]]+num[i]); 
					if(dp[j]<dp[j-num[i]]+num[i]){
						dp[j]=dp[j-num[i]]+num[i];
					}else{
						dp[j]=dp[j];
					}
				} 
			}
			cout<<mony-dp[mony-5]-num[n-1]<<endl;  
		} 
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值