蓝桥杯 第七讲 贪心
一、题目特点
- 跳跃性很强
- 结论证明很难
二、解题策略
- 找相似
- 猜想
AcWing 1055. 股票买卖 II
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int p[N],ans,n;
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &p[i]);
}
for (int i = 0; i < n-1; i ++ )
{
if(p[i+1] > p[i]) ans += p[i+1] - p[i];//贪心策略:只要比前一天价格高,就买入
}
cout << ans << endl;
return 0;
}
AcWing 104. 货仓选址
中位数有非常优秀的性质,比如说在这道题目中,每一个点到中位数的距离,都是满足全局的最有性,而不是局部最优性。
具体的来说,我们设在仓库左边的所有点,到仓库的距离之和为p,右边的距离之和则为q,那么我们就必须让p+q的值尽量小。
当仓库向左移动的话,p会减少x,但是q会增加n−x,所以说当为仓库中位数的时候,p+q最小。
还是同样的一句话,画图理解很重要。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N],n;
LL ans;
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &a[i]);
}
sort(a,a+n);
for (int i = 0; i < n; i ++ )
{
ans += abs(a[n>>1] - a[i]);
}
cout << ans <<endl;
return 0;
}
122. 糖果传递
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int a[N],c[N];
int n;
LL ave,ans;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
ave += a[i];
}
ave /= n;
for(int i = n;i>1;i--)
{
c[i] = c[i+1] + ave - a[i];
}
c[1] = 0;
sort(c+1,c+n+1);
for (int i = 1; i <= n; i ++ )
{
ans += abs(c[i] - c[(i+1)/2]);
}
cout << ans << endl;
return 0;
}
112. 雷达设备
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1010;
struct Range
{
double l;
double r;
bool operator<(struct Range& t) const
{
return r < t.r;
}
}range[N];
int n,d,x,y;
int main()
{
scanf("%d%d", &n,&d);
for (int i = 0; i < n; i ++ )
{
scanf("%d%d", &x, &y);
if(y > d) //如果y大于检测半径,则无法覆盖到
{
cout << -1 << endl;
return 0;
}
double t = sqrt(d*d - y*y); //安置能够监测到该小岛的雷达的范围,勾股定理
range[i] = {x-t,x+t};
}
sort(range,range+n); //区间选点思路做法,右端点排序
int ans = 1;
double ed = range[0].r;
for (int i = 1; i < n; i ++ )
{
double l = range[i].l,r = range[i].r;
if(l > ed) //若当前区间左端点比上一个选出的区间的左端点大,说明必须再拿一个点表示该区间
{
++ans;
ed = r;
}
//否则的话就舍弃该区间
}
cout << ans << endl;
return 0;
}
1235. 付账问题
升序排序后,均摊当前剩下的费用,带钱少的有多少付多少
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int n;
double s;
int a[N];
double ave,ans;
int main()
{
scanf("%d%lf", &n,&s);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &a[i]);
}
sort(a,a+n); //升序排序
ave = 1.0*s/n;
for (int i = 0; i < n; i ++ )
{
double cur = 1.0*s/(n-i); //均摊当前剩余款项
if(a[i] < cur) cur = a[i]; //钱不够的有多少付多少,钱够的付平均值
ans += (cur - ave) * (cur - ave);
s -= cur;
}
ans = 1.0*ans/n;
printf("%.4lf\n",sqrt(ans));
return 0;
}
1239. 乘积最大
C++中,正数取模正数是正数,负数取模正数是负数,取模的数的正负对结果无关
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10,MOD = 1000000009;
typedef long long LL;
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);
LL ans = 1;
int l = 0,r = n-1;
int sign = 1; //判断整个数组是否全为负数,是为-1,否则为1
if(k%2 == 1) //选奇数个数
{
ans = a[r--]; //此情况下,最大值必选
--k;
if(ans < 0) sign = -1; //最大值是负数,整个数组全是负数
}
//如果选偶数个,成对选择乘积较大的即可
while(k)
{
LL x = (LL)a[l] * a[l+1],y = (LL)a[r] * a[r-1]; //选取左右两组数
if(x * sign < y * sign) //如果全为负数,就要选绝对值较小的去乘,否则选绝对值较大的去乘
{
ans = y % MOD * ans % MOD; //一定要先取余,后相乘
r-=2;
}
else
{
ans = x % MOD * ans % MOD; //一定要先取余,后相乘
l+=2;
}
k-=2;
}
cout << ans << endl;
return 0;
}
1247. 后缀表达式
逆波兰表达式可以通过"加括号"的方式调整运算顺序,即把负号变成正号,尽可能的给每个负数配一个负号,给每个正数配一个正号
至少减一个数,加一个数,贪心策略就是:减最小的,加最大的
一般情况:把所有数排个序,最大的拿出来,放首项,把最小的数拿出来,给他一个减号,再套一个括号,那么现在还未完成的表达式长这样:
可以发现,现在如果我想加一个数的话,给它一个加号,放在括号外面,也可以给它一个减号,放在括号里面;减一个数同理。换句话说,只要用一个减号,一个最大值,一个最小值,其他数我想加就加,想减就减。那么为了使结果最大,我加上正数,减去负数,就是直接加上所有剩下数的绝对值,那么就解决了。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int a[2*N],n,m; //注意数的个数可以为两倍的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 ans = 0;
if(m==0) //没有减号,直接全加
{
for (int i = 0; i < k; i ++ )
{
ans += a[i];
}
}
else //有减号
{
sort(a,a+k); //也可以不排序,直接找最值
ans = a[k-1] - a[0]; // 贪心策略:加上最大值,减去最小值
for (int i = 1; i < k-1; i ++ ) //其余的数想加就加,想减就减,贪心策略:负数就减,正数就加
{
ans += abs(a[i]);
}
}
cout << ans << endl;
return 0;
}
1248. 灵能传输
由a[i] = s[i] - s[i-1]
与题意,要让a[i]的绝对值尽可能小,就要让前缀和数组s两两之间的差的绝对值尽可能小
贪心策略:对前缀和数组进行冒泡排序
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 300005;
int T;
LL a[N],s[N],n;
bool st[N];
int main()
{
scanf("%d", &T);
while (T -- )
{
scanf("%d", &n);
s[0] = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%lld", &a[i]);
s[i] = s[i-1] + a[i];
}
LL s0 = s[0],sn = s[n];
if(s0 > sn) swap(s0,sn);
sort(s,s+n+1);
for (int i = 0; i < n; i ++ )
{
if(s[i] == s0)
{
s0 = i;
break;
}
}
for (int i = n; i >= 0; i -- )
{
if(s[i] == sn)
{
sn = i;
break;
}
}
memset(st, 0, sizeof st);
int l = 0,r = n;
for(int i = s0;i>=0;i-=2)
{
a[l++] = s[i];
st[i] = true;
}
for(int i = sn;i<=n;i+=2)
{
a[r--] = s[i];
st[i] = true;
}
for (int i = 0; i <= n; i ++ )
{
if(!st[i])
{
a[l++] = s[i];
}
}
LL res = 0;
for (int i = 1; i <= n; i ++ ) res = max(res,abs(a[i] - a[i-1]));
cout<<res<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)