文章目录
写在前面
最近开始学习算法,备战算法比赛。此文是对贪心法的初次学习。如有错误请多多包涵,并予以指正。谢谢!学习整理不易,如果你觉得我写的不错,请点赞支持下博主,十分感谢!
贪心法概括
贪心法就是遵循某种规则,不断贪心的选取当前最优策略的算法设计方法。
硬币问题
问题描述
有1元、5元、10元、50元、100元、500元的硬币各C1、C5、C10、C50、C100、C500枚,先要用这些硬币来支付A元,最少需要多少枚硬币?假定本题存在解。
输入
C1=3,C2=2,C10=1,C50=33,C100=0,C500=2,A=620
输出
6
算法思路
这道题比较贴近生活,在生活中我们解决这个问题一般是先找最大面值可以用几个。。以此类推
代码实现
#include<iostream>
using namespace std;
const int V[6] = {1, 5, 10, 50, 100, 500};//记录面值
int C[6];//存储有多少硬币
int A;//金额
void solve()
{
int ans = 0;
for (int i = 5; i >= 0;--i)
{
int t = min(A / V[i], C[i]);//当前最大使用的硬币数,和能够使用的硬币数的比较,防止用多硬币
A -= t * V[i];
ans += t;
}
cout << ans << endl;
}
区间问题
问题描述
有n项工作,每项工作分别在si开始ti结束。对于每项工作,你都可以选择参与与否。如果选择参与,则这段时间不可以干其他工作,尽可能的完成多的工作,那么最多能参与多少哦项工作
输入
n = 5
s = {1,2,4,6,8}
t = {3,5,7,9,10}
输出
3
算法思路
这道题的贪心算法思路不好想,一共有三种情况
情况1
在可选的工作中,每次都选取结束最早的工作
情况2
在可选的工作中,每次都选取用时最短的工作
情况3
在可选的工作中,每次都选取重叠工作最少的工作
情况分析
情况1是正确的,每次结束都最早,可以选的工作就越多,
情况2和情况3都有反例。
反例1,情况2中用时最短的工作恰好在中间且与2中工作重叠。
----- -----
----
反例2,情况3中当前最少重叠工作恰好在中间。与多个工作重叠。
------ ------
------ ------
-----
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 100000;
int N;
int S[MAX_N];
int T[MAX_N];
pair<int, int> itv[MAX_N];
void solve()
{
for (int i = 0; i < N;++i)
{
itv[i].first = T[i];//结束时间
itv[i].second = S[i];//开始时间
}
sort(itv, itv + N);//根据结束时间,找出结束时间相对较短的工作
int ans = 0;
int t = 0;
for (int i = 0; i < N;++i)
{
if(t < itv[i].second)//如果现在可以开始这项工作
{
ans++;
t = itv[i].first;//加上结束时间
}
}
cout << ans << endl;
}
Best Cow Line(POJ3617)
给定长度N的字串S 构造长度N的字串T,字串T一开始为空串,随后对字串S进行任意操作。
从S头删除一个字符,加到T的尾部
从S尾删除一个字符,加到T的尾部
目标构建字典序尽可能小的T
输入
N = 6
S =“ACDBCB”
输出
ABCBCD
算法思路
将S翻转,通过两个头进行比较,将比较小的字符插入进字串,每次比较相对较小,自然构建的字串T也是较小
代码实现
#include<iostream>
using namespace std;
int N;
char S[10000];//存字符串
void solve()
{
int a = 0;
int 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--]);
}
}
cout << endl;
}
Saruman’s Army(POJ 3069)
直线上有N个点,点i的位置是Xi。从这N个点选若干个,加上标记,并在这些标记点的R范围内,能够包括全部点
输入
N =6
R = 10
X = (1 , 7, 15, 20, 30, 50)
输出
3
算法思路
大致思路是,遍历这些点,计数一次就代表,从第i个点开始找到最大距离那个点然后根据最大距离那个点在找最大距离那个点,然后标记中间那个点,计数++ 。再从上次为类似第一个点 进行所有点的查找。得到的结果就是一共需要标记多少个点。备注,如果找不到则标记此处,并从下一个点开始继续操作
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
int N;
int R;
int X[1000];
void solve()
{
sort(X, X + N);
int i = 0;
int ans = 0;
while(i>N)
{
int s = X[i++];//标记初始点
while(i < N && X[i] <= s + R)
{
i++;
}
int p = X[i - 1]; //标记中间点
while(i < N && X[i] <= p + R) //找最远的那个点
{
i++;
}
ans++; //标记点+1 并且以中间点找到最远的那个点为下一个初始点进行进一步迭代
}
cout << ans << endl;
}
Fence Repair
问题描述
要将一块很长的木板切割成N块,长度为L1 ,L2 … LN 求最小开销是多少
(最小开销,如样例8 ,5,8则最小开销为 (5 + 8)+(5+8)+8 =34 )
输入
N = 6
L = {8 ,5, 8}
输出
34
算法思路
用贪心法,先将L升序排序,然后从头开始遍历,即为最小开销。(局部最小开销,全局最小开销)
#include<iostream>
#include<algorithm>
using namespace std;
int array[100];
int main()
{
int N = 0;
cin >> N;
for (int i = 0; i < N;++i)
{
cin >> array[i];
}
sort(array, array + N);
int sum = array[0];
int allsum = 0;
for (int i = 1; i < N;++i)
{
sum += array[i];
allsum += sum;
}
if(N==1)
{
allsum += sum;
}
cout << allsum << endl;
return 0;
}