第三周的重点是贪心法,双指针,前缀和,差分法。
贪心法
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
例题 :P5602 小 E 与美食 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
小 E 有 nn 种美食可供选择,每种美食只能吃一次,第 ii 种美食有一个美味值 aiai,吃下一个美味值为 aiai 的美食可以让小 E 的满足感提升 aiai。
但是小 E 的胃是有极限的,每吃下一个美食,他的饱腹感就会提升 11。
小 E 最后的舒适度是他的满足感的平方除以他的饱腹感,你的目标是求出他舒适度能达到的最大值。
要求出舒适度最大的情况就要先将美食的幸福值从高到底排序,优先吃满足感高的食物。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N =1e6 + 10;
int c[N];
int s[N];
bool cmp(int a,int b){
return a>b;
}
signed main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> c[i];
}
sort(c+1,c+n+1,cmp);
for(int i = 1; i <= n; i++){
s[i] = s[i - 1] + c[i];
}
double maxn = -1.0;
for(int i = 1; i<= n; i++){
double temp = s[i] / i * s[i];
maxn=max((double)s[i]*s[i]/i,maxn);
}
printf("%lf",maxn);
}
双指针
双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。双指针的要点关键词就是维持区间。
例题:A-B 数对 - 洛谷
给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A−B=C A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
本题的思路就是先将数列从小到大排序,然后用P指针遍历队列,每次移动P指针之前用A,B两个指针向后探索符合A-B=C的数字区间然后将这个区间之间的数字的个数加到数对个数的总和中。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
int n , c;
int a[N];
int main ()
{
cin >> n >> c;
for(int i = 1 ; i <= n ; i ++) cin >> a[i];
sort(a + 1 , a + 1 + n);
int l = 1, r1 = 1 , r2 = 1;
ll ans = 0;
for(l = 1 ; l <= n ; l ++) {
while(r1 <= n && a[r1] - a[l] <= c){
r1 ++;
}
while(r2 <= n && a[r2] - a[l] < c ){
r2 ++;
}
if(a[r2] - a[l] == c && a[r1 - 1] - a[l] == c && r1 - 1 >= 1){
ans += r1 - r2;
}
}
cout << ans;
}
前缀和,差分法
前缀和指一个数组的某下标之前的所有数组元素的和(包含其自身)。前缀和分为一维前缀和,以及二维前缀和。前缀和是一种重要的预处理,能够降低算法的时间复杂度。可以快速地求出某一段的和。所谓差分可以理解为前缀和的逆运算,就是将数列中每一项分别与前一项做差。
例题:语文成绩 - 洛谷
语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?
本题就是差分法和前缀和的综合运用,先用差分法差分数组,然后确定加分区间A和B,在A上+1,在B+1上-1。之后用前缀和得出在A到B区间上都加一后的数列。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e9 + 10;
int c[N];
int f[N];
int a[N];
int main(){
int n,p;
cin >> n >> p;
for(int i = 1; i <= n; i++){
cin >> a[i];
f[i] = a[i] - a[i-1];
}
int x,y,z;
for(int i = 1; i <= p; i++){
cin >> x >> y >> z;
f[x]+=z;
f[y+1]-=z;
}
int minn = 100000;
for(int i = 1; i <= n; i++){
c[i] = c[i-1] + f[i];
minn = min(minn,c[i]);
}
cout << minn;
}