贪心算法的几个经典问题
贪心算法:贪心法顾名思义就是不断贪心的选取当前最优策略的计算方法。
下面介绍几种贪心问题
问题一:货币选择问题
问题描述
:分别有1,5,10,50,100元,分别有5,2,2,3,5张纸币。问若要支付k元,则需要多少张纸币?
问题分析
:
我们只需要遵循“优先使用面值大的硬币”即可。
1.尽可能多的使用100元(即最大的);
2.余下部分尽可能多的使用50元;
3.余下部分尽可能多的使用10元;
4.余下部分尽可能多的使用5元;
5.余下部分使用1元;
代码如下:
-
#include <iostream>
-
#include <algorithm>
-
using
namespace
std;
-
-
const
int N=
5;
-
int Money[N]={
5,
2,
2,
3,
5};
-
int Value[N]={
1,
5,
10,
50,
100};
-
-
int solve(int money){
-
int num=
0;
-
for(
int i=N
-1;i>
0;i--){
-
//c为使用纸币的张数,在需要用面值为vaule[i]的张数和已有张数里选取最小的;
-
int c=min(money/Value[i],Money[i]);
-
money=money-Value[i]*c;
-
num+=c;
-
}
-
if(money>
0){
-
num=
-1;
-
}
-
return num;
-
}
-
int main(){
-
int money;
-
cin>>money;
//输入一共需要支付多少钱
-
int res=solve(money);
-
if(res!=
-1){
-
cout<<res<<
endl;
-
}
else{
-
cout<<
"no"<<
endl;
-
}
-
return
0;
-
-
}
-
结果:
若要支付520元,则需要7张纸币。
问题二:区间调度问题
问题描述
:有n项工作,每项工作分别在Si开始,Ti结束。例如S={1,2,4,6,8},T={3,5,7,8,10}。对每项工作,你都可以选择与否,若选择参加,则必须至始至终参加全程参与,且参与工作的时间段不能有重叠。(如下图)
问题分析
:
我们把“在可选工作中,每次都选取结束时间最早的”策略作为贪心算法所遵循的规则。
例如,输入n=5,S={1,2,4,6,8},T={3,5,7,9,10};输出:3(选取工作为1,3,5)
代码如下:
-
#include <stdio.h>
-
#include <tchar.h>
-
#include <queue>
-
#include "iostream"
-
-
using
namespace
std;
-
//输入
-
const
int n =
5;
-
int S[n]={
1,
2,
4,
6,
8};
-
int T[n]={
3,
5,
7,
9,
10};
-
-
pair<
int,
int> itv[n];
//对工作排序的pair数组
-
int solve()
-
{
-
//对pair进行字典序比较
-
//为了让结束时间早的工作排在前面,把T存入first,把S存入second
-
for(
int i =
0; i < n; i ++) {
-
-
itv[i].first = S[i];
-
itv[i].second = T[i];
-
}
-
sort(itv, itv + n);
-
-
int count =
0;
//选取的结果
-
int t =
0;
//最后所选工作的结束时间
-
for(
int i =
0; i < n; i ++) {
-
if(t < itv[i].first) {
-
count ++;
-
t = itv[i].second;
-
}
-
}
-
return count;
-
}
-
-
int main() {
-
int k=solve();
-
cout << k<<
endl;
-
return
0;
-
}
问题三:字典序最小问题
问题描述:
给定长度为N的字符串S,要构造一个长度为N字符串T。T是一个空串,反复执行下列任意操作:
->从S的头部删除一个字符,加到T的尾部;
->从S的尾部删除一个字符,加到T的尾部;
目标是要构造字典序尽可能小的字符串T。
PS:字典序是指从前到后比较两个字符串的大小的方法。首先比较第1个字符,如果不同则第1个字符较小的字符串更小,如果相同则继续比较第2个字符……反复继续,来比较整个字符串的大小。
问题分析:
从字符串性质上看,无论T的末尾多大,只要前面部分的较小即可。
把‘不断取S得开头和末尾中较小的一个字符放到T的末尾’作为贪心法所遵循的策略。
我们可以有以下步骤:
1.按照字典序比较S和将S反转后的字符串S’。
2.如果S较小,从S的开头取出一个字,追加到T的末尾。
3.如果S’较小,从S的末尾取出一个字,追加到T的末尾。(如果相同,则取谁都可以)
例如:
输入 ‘ACDBCB’,
输出 ‘ABCBCD’
代码如下:
-
#include<cstdio>
-
#include<cstring>
-
#include<algorithm>
-
#include<iostream>
-
using
namespace
std;
-
int main()
-
{
-
//输入
-
int n=
6;
-
char S[
7]=
"ACDBCB";
-
int a=
0,b=n
-1;
-
while(a<=b){
-
-
bool left=
false;
-
//把从左起和从右起的字符串比较
-
for(
int i=
0;a+i<=b;i++){
-
if(S[a+i]<S[b-i]){
-
left=
true;
-
break;
-
}
else
if(S[a+i]>S[b-i]){
-
left=
false;
-
break;
-
}
-
}
-
//左右两边谁大输出谁
-
if(left)
putchar(S[a++]);
-
else
putchar(S[b--]);
-
}
-
-
return
0;
-
}
-
-
代码仅供参考,让我们共同学习!