贪心:局部最优解映射到全局最优解(玄之又玄)
目录
股票买卖 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)基础公式:
每位小朋友所持有的糖果数量==原有的糖果数量-他给别人的糖果数量+别人给他的糖果数量
此时想象下图的这个环,代表着小朋友们交换糖果的情况
(2)要使得小朋友们交换糖果的次数最少(一次一个糖果),且满足题意,那么由于总的糖果数量是不变的,那么必然游戏结束之后,每位小朋友所持有的糖果数量一定是糖果数量数量的平均值
那么就有这样一个公式:
(3)目标转换:
由上已知我们要求的目标已经转换为了求|x1|+|x2|+|x3|+……+|xn|的最小值
(4)公式转换:
那么这道题就转换为了货仓选址问题
#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;
}