开始复习计划~~
一些算法。
基础算法
二分
我一直写不对的算法。。。
网上讲的二分都太杂了,现在我们把二分的边界条件全设为如下模式(向FALLEN_GEMINI大佬学习):
while(l<r)
{
mid=·····;
······;
}
此时如果我们要求的是较大值,则中间内容如下:
while(l<r)
{
mid=(l+r+1)>>1;
if(check(mid))
l=mid;
else
r=mid-1;
}
反之,较小值则如下:
while(l<r)
{
mid=(l+r)>>1;
if(check(mid))
r=mid;
else
l=mid+1;
}
实数上的二分加个精度判断就好:
while(l+eps<r)
{
double mid=·····;
······;
}
关于二分题的类型,一般有如下特征:
- 最大值最小或最小值最大;
- 含有特定的单调性;
- 数据范围一般较大(毕竟二分时间复杂度才
O(logN))。
关于几个二分的题:
一道经典的二分答案题,我们二分最大的距离,然后判断距离超过这个距离的瓶盖到没到达
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
long long n,m;
long long a[101010];
bool check(long long x)
{
long long cnt=1;
long long num=1;
for(int i=2;i<=n;i++)
if(a[i]-a[num]>=x)
{
cnt++;
num=i;
}
if(cnt>=m)
return true;
return false;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
sort(a+1,a+n+1);
long long l=1,r=a[n]-a[1];
long long mid;
while(l<r)
{
mid=(l+r+1)>>1;
if(check(mid))
l=mid;
else
r=mid-1;
}
printf("%lld\n",l);
return 0;
}
接着二分中有一类三分法,可以解决那种单峰或单谷问题,这个我之前写过,放几个链接在这里:
然后是关于离散化,这个我老是记不住,总之就是排序,去重,再二分:
int main()
{
int a[],b[],q[],n;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
b[i]=a[i];//复制
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-b-1;//排序加去重
for(int i=1;i<=n;i++)
q[i]=lower_bound(b+1,b+cnt+1,a[i])-b;//二分查找下标
}
倍增
和二分一样优秀的算法,就是每次遍历的长度较上一次增加一倍,与二进制相结合可以做出很多题。
倍增最重要的是它的 ST 算法,在 RMQ 问题中能够在 O(NlogN) 的预处理后,O(1) 在线回答任何区间内的最值。
算法思想如下:
设
然后用倍增的思想递推出
就是把区间分成两半,取左右两半的最大值。
于是得到代码:
for(int i=1;i<=n;i++)
f[i][0]=a[i];
for(int j=1;j<=20;j++)
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
题目:[CF1237D] Balanced Playlist
贪心
这个要重点讲讲,因为我做的题比较少。
反正就是以下几点:
- 选最优的;
- 保证整体最优由局部最优得到
有几个经典例题要看看:
选择不相交区间:给
按照
区间选点问题:给
按照
区间覆盖问题:给
按照
流水作业调度问题:有
对于
对于
然后第一个作业集合接上第二个作业集合就是最优顺序。
带限期和罚款的单位时间任务调度问题:
将
前缀和与差分
这个不用多讲吧。。。主要就是在于优化和数据结构的搭配。
跳过
图论
最短路
Floyd
简单的 Floyd,三层循环即可:
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
Floyd 还能用来传递闭包,用来传递一些信息:
dis[x][y]=dis[y][x]=1;//初始化
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]|=dis[i][k]&dis[k][j];