2.26网课笔记(贪心)
1.贪心算法是什么?
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。 贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。也就是说,不从整体最优上加以考虑,做出的只是在某种意义上的局部最优解。
2.贪心算法经典题(我认为的)
2.1. 月饼
又到了我最喜欢的那道题:
PTA1020月饼
根据题意,我们可以很直接得得知这道题应该使用贪心算法,即每次都选择先出售掉价值最高的月饼(因为月饼的需求总量是不变的,不会因为我们卖了贵月饼就少要一点......)
思路就是:算出每种月饼的单价,然后把单价最高的优先卖出去,同时需求量要减去每种月饼的数量,如果需求小于该类月饼的库存,则用单价去×剩下的需求量
c++代码如下:
#include <bits/stdc++.h>
using namespace std;
struct mooncake
{
double store;
double sell;
double price;
} cake[1005];
bool cmp(mooncake a, mooncake b)
{
return a.price > b.price;
}
int main()
{
int n, d;
double ans = 0;
cin >> n >> d;
for (int i = 0; i < n; i++)
{
cin >> cake[i].store;
}
for (int i = 0; i < n; i++)
{
cin >> cake[i].sell;
cake[i].price = cake[i].sell / cake[i].store;
}
sort(cake, cake + n, cmp);
for (int i = 0; i < n; i++)
{
if (cake[i].store <= d)
{
d -= cake[i].store;
ans += cake[i].sell;
}
else
{
ans += cake[i].price * d;
break;
}
}
printf("%.2f", ans);
return 0;
}
2.2.石头堆
先看题!
P1090合并果子
依然是很简单的贪心,我们每次都选择最小的两组石头来进行合并。首先写入数据,然后把排一次序
随后,我们每次都操作这个列表的前两项,ans累加所需的力气,然后再把第一位和第二位进行合并,将其插入到合理的位置。切记!不能每次操作后都对数组进行一次sort排序(必超时)
c++代码
#include <bits/stdc++.h>
using namespace std;
int n, ans;
int arr[100000];
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
sort(arr, arr + n);
for (int i = 0; i <= n-2; i++)
{
ans += (arr[i] + arr[i + 1]);
arr[i + 1] += arr[i];
for (int j = i+1 ; j <= n-2; j++)
{
if(arr[j] > arr[j + 1])
swap(arr[j], arr[j + 1]);
else
break;
}
}
cout << ans;
return 0;
}
当然,我们有更优雅的STL库方法来实现(这道题完美的契合了它!)
优先队列
每次都选择两个最小的来合并,然后把合并好的添加进原有的序列中,并且无论怎样每次都是选择两个最小的,我打开这道题一看,满篇都写着"优先队列"
c++代码如下:
#include <bits/stdc++.h>
using namespace std;
//优先队列方法~
priority_queue<int, vector<int>, greater<int>> q; //升序,从小到大
//priority_queue<int, vector<int>, less<int> > q; //降序,从大到小
int n, temp, ans;
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> temp;
q.push(temp);
}
while (q.size() >= 2)
{
int a = q.top();
q.pop();
int b = q.top();
q.pop();
ans += (a + b);
q.push(a + b);
}
cout << ans;
return 0;
}
2.3 排列出的最小数
看题!
PTA B1023 组个最小数
策略很简单:先从1-9中选择一个最小的数输出(因为0不能开头) ,然后从0-9输出数字,每个数字输出次数为其剩余个数(贪心)
证明正确性:首先,由于所有的数字都必须参与组合,因此最后结果的位数是确定的。然后,由于最高位不能为0,所有从除0外的最小数来输出(如果存在两个长度相同的数的最高位不相同,那么一定是最高位小的数更小。)最后,针对除最高位外的所有位,也是从高位到低位优先选择[0,9]中还存在的最小的数输出
这道题很简单,大家现在的水平足矣做出来。
c++代码如下:
#include <bits/stdc++.h>
using namespace std;
int a[15], temp;
int main()
{
for (int i = 0; i <= 9; i++)
{
cin >> a[i];
}
for (int i = 1; i <= 9; i++)
{
if (a[i] != 0)
{
temp = i;
cout << i;
a[i]--;
break;
}
}
while (a[0]--)
cout << '0';
for (int i = temp; i <= 9; i++)
{
while (a[i]--)
cout << i;
}
return 0;
}
2.4 买卖股票系列第一题
思路:最小的时候买进,最大的时候卖出。仔细讲讲实现
java代码:
class Solution {
public int maxProfit(int[] prices) {
int max=0,min;
if(prices.length <= 1) return 0;
min = prices[0];
for(int i = 1; i < prices.length;i++){
if(prices[i] < min){
min = prices[i];
}else{
max = prices[i] - min > max?prices[i]-min : max;
}
}
return max;
}
}
c++:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() <= 1) return 0;
int min1 = prices[0],max1;
for(int i = 1;i < prices.size();i++){
if(prices[i] < min1){
min1 = prices[i];
}else{
max1 = prices[i] - min1 > max1 ? prices[i] - min1: max1;
}
}
return max1;
}
};
2.5皮一下很开心
思路:畅所欲言。大家一起讨论~~
3.什么时候用贪心?
贪心最重要的特点:
当你的选择对后续的选择不产生影响的时候,就贪心
这种情况就不能用!
https://www.cherryg.cn/detail/3
//经典入门DP问题:01背包

浙公网安备 33010602011771号