[AcWing蓝桥杯]之贪心(C++题解)

贪心:局部最优解映射到全局最优解(玄之又玄)


目录

股票买卖 II

货仓选址

糖果传递(数论)

雷达设备

 付账问题

乘积最大

后缀表达式(逆波兰求值数论做法)


股票买卖 II

1055. 股票买卖 II - AcWing题库

核心思路:

只要后一个比前一个大,那么我就是赚的,直接卖出即可,一点点的蝇头小利,映射到全局就是最优解

 代码实现:

#include
#include
using namespace std;
const int N=1e5+10;
int nums[N];
int n;

int main()
{
    scanf("%d",&n);
    for(int i=0;i0)
        {
            res+=nums[i]-nums[i-1];
        }
    }
    printf("%d\n",res);
    
    return 0;
}

货仓选址

104. 货仓选址 - AcWing题库

核心思路:

先求出平均值,再遍历每一个数到平均值的距离即可

关键是平均值在极端情况下可能爆long long,那么这个时候就不能用传统的叠加,再除以个数求平均值了,此时需要将数组进行排序,取中间值即可,这个数也是平均值

#include
#include
#include
#include
using namespace std;
const int N=1e6+10;
int nums[N];
int n;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i

糖果传递(数论)

122. 糖果传递 - AcWing题库

核心思路:

(1)基础公式:

每位小朋友所持有的糖果数量==原有的糖果数量-他给别人的糖果数量+别人给他的糖果数量

 此时想象下图的这个环,代表着小朋友们交换糖果的情况

[AcWing蓝桥杯]之贪心(C++题解)_第1张图片

(2)要使得小朋友们交换糖果的次数最少(一次一个糖果),且满足题意,那么由于总的糖果数量是不变的,那么必然游戏结束之后,每位小朋友所持有的糖果数量一定是糖果数量数量的平均值

那么就有这样一个公式:

[AcWing蓝桥杯]之贪心(C++题解)_第2张图片

 (3)目标转换:

由上已知我们要求的目标已经转换为了求|x1|+|x2|+|x3|+……+|xn|的最小值 

(4)公式转换:

[AcWing蓝桥杯]之贪心(C++题解)_第3张图片

 那么这道题就转换为了货仓选址问题

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1000010;

int n;
int a[N];
LL c[N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);

    LL sum = 0;
    for (int i = 1; i <= n; i++) sum += a[i];

    LL avg = sum / n;
    for (int i = n; i > 1; i--)
    {
        c[i] = c[i + 1] + avg - a[i];//构造公式
    }
    c[1] = 0;

    sort(c + 1, c + n + 1);

    LL res = 0;
    for (int i = 1; i <= n; i++) res += abs(c[i] - c[(n + 1) / 2]);//货仓选址

    printf("%lld\n", res);

    return 0;
}

雷达设备

112. 雷达设备 - AcWing题库

#include
#include
#include
#include
#include
using namespace std;

const int N = 1010;
int n, d;
struct Segment {
	double l, r;
	bool operator< (const Segment& t) const//按右端点进行排序
	{
		return r < t.r;
	}
}seg[N];

int main()
{
	cin >> n >> d;

	bool failed = false;
	for (int i = 0; i < n; i++)
	{
		int x, y;
		cin >> x >> y;
		if (y > d) failed = true;
		else
		{
			double len = sqrt(d * d - y * y);
			seg[i].l = x - len;//左端点
			seg[i].r = x + len;//右端点
		}
	}
	if (failed)//如果纵坐标已经大于了探测范围,则该点不可能被探测
	{
		puts("-1");
	}
	else
	{
		sort(seg, seg + n);//按右端点进行排序
		int cnt = 0;//雷达个数
		double last = -1e20;
		for (int i = 0; i < n; i++)//判断上一个的右端点是否大于新区间的左端点
		{
			if (last < seg[i].l)//如果上一个的右端点小于新区间的左端点,那么证明无法覆盖,就另起一个新的雷达点
			{
				cnt++;
				last = seg[i].r;
			}
		}
		printf("%d\n", cnt);
	}
	

	return 0;
}

 付账问题

1235. 付账问题 - AcWing题库

题意概述:让每个人出的钱的标准差最小

贪心:假设这段饭的费用为S,那么平均费用为S/n,但是有的人没带够这么多钱,那么贪心思想:他要付出所有的钱,来使得标准差最小,剩余部分的钱,每个人多出一点补上即可

#include
#include
#include
#include
using namespace std;
const int N = 500010;
int n;
int a[N];

int main()
{
	long double s;
	cin >> n >> s;
	for (int i = 0; i < n; i++ ) scanf("%d", &a[i]);
	sort(a, a + n);

	long double res = 0;
	long double avg = s / n;
	
	for (int i = 0; i < n; i++)
	{
		double cur = s / (n - i);//当前的平均费用
		if (a[i] < cur) cur = a[i];//如果这个人当前的前小于当前的平局费用,那么就把它的所有钱贡献出来
		res += (cur - avg) * (cur - avg);//方差
		s -= cur;//总钱数-已经支付的钱数
	}

	printf("%.4Lf\n", sqrt(res / n));//精度问题

	return 0;
}

乘积最大

1239. 乘积最大 - AcWing题库

同样是模拟过程:

分为取的数字个数为计数和偶数情况讨论

如果k是奇数,那么先按照排序,取最大的数,保存到res中,这个准没错

之后就是循环k/2次了,每次两个数两个数的取,因为已经排好序了,所以是两个数的乘积和两个数的乘积相比较

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N = 100010, mod = 1000000009;

int n, k;
int a[N];

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    sort(a, a + n);

    int res = 1;
    int l = 0, r = n - 1;
    int sign = 1;
    if (k % 2)
    {
        res = a[r--];
        k--;
        if (res < 0) sign = -1;
    }
    while (k)
    {
        LL x = (LL)a[l] * a[l + 1], y = (LL)a[r - 1] * a[r];
        if (x * sign > y * sign)
        {
            res = x % mod * res % mod;
            l += 2;
        }
        else
        {
            res = y % mod * res % mod;
            r -= 2;
        }
        k -= 2;
    }

    printf("%d\n", res);

    return 0;
}

后缀表达式(逆波兰求值数论做法)

1247. 后缀表达式 - AcWing题库

数论:多个负号的情况,把它变成只有一个符号的情况,即:

正数+正数+正数-(数-数-数),此时得到就是最大值,并且用到了多个负号

原理:

AcWing 1247. 后缀表达式 - AcWing

#include
#include
#include
#include
using namespace std;

typedef long long ll;
const int N = 200010;
int n, m;
int a[N];

int main()
{
	scanf("%d%d", &n, &m);
	int k = n + m + 1;

	for (int i = 0; i < k; i++) scanf("%d", &a[i]);

	ll res = 0;
	if (!m)
	{
		for (int i = 0; i < k; i++) res += a[i];
	}
	else
	{
		sort(a, a + k);

		res = a[k - 1] - a[0];
		for (int i = 1; i < k - 1; i++) res += abs(a[i]);
	}

	printf("%lld\n", res);

	return 0;
}

你可能感兴趣的:(AcWing蓝桥杯,c++,蓝桥杯,贪心算法)