To_Heart—总结——所学知识总结
前言
为了完成自己立下的flag,笔者决定开一篇博客,将自己所学过的所有板块进行梳理,也许工程量有些浩大,但笔者相信,自己一定可以成功。
基本算法
贪心
通过局部最优解构成全局最优解
例题
题解
纸牌总数是一定的,把牌堆看成一个序列,每个堆最后的纸牌数都应该是这个序列的平均数。所以就统计那些牌堆不为平均数即可。
#include<bits/stdc++.h>
using namespace std;
int a[105],sum;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
sum/=n;
for(int i=1;i<=n;i++){
a[i]-=sum;
}
sum=0;
for(int i=1;i<=n;i++){
if(!a[i])
continue;
a[i+1]+=a[i];
sum++;
}
cout<<sum;
return 0;
}
二分查找
首先,放句名言
90%的程序员无法正确实现二分查找算法
二分主要利用了序列的单调性。假设一个单调递增的序列,那么其中的任何一个元素都比前面的所有元素大,都比往后的所有元素小。所以我们可以折半查找某一元素
二分可以在O(logn)的复杂度中查找序列的某一元素。
模板
int l=0,r=n+1,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(/*自己写条件*/)
l=mid-1,ans=mid;
else
r=mid+1;
}
搜索
不会吧,不会吧,不会真的有人连搜索都打不来吧
DP
不会吧,不会吧,不会真的有人连DP都会打吧
因为搜索和DP的知识太多了,所以笔者就单独把它们列出来。
前缀和
前缀和可以用O(n)的时间复杂度对数组进行预处理,然后再O(1)的时间之类求出任意两个元素之间的元素和。
我们可以用一个数组sum来计算前缀和,其中 s u m i sum_i sumi表示前i项的元素和。
所以很容易可以得到: s u m i = s u m i − 1 + a i sum_i=sum_{i-1}+a_i sumi=sumi−1+ai ,其中 a i a_i ai表示当前元素的值。
如果我们要查找第i项到第j项的前缀和,就可以看成求 1 ∼ j 1 \sim j 1∼j项的前缀和再减去 1 ∼ i − 1 1 \sim i-1 1∼i−1项的前缀和,所以就可以表示成 s u m i − s u m j − 1 sum_i-sum_{j-1} sumi−sumj−1。
数据结构
并查集
可以在log的时间复杂度中判断出两个元素是否在一个集合。
具体实现可以参考这篇博客
当然,我还是要提一提
现在假设有一堆不同的元素,每几个元素就组成一个集合。
我们在每一个集合当中选出一个元素作为代表,那么只要两个元素所在集合的代表相同,它们就在同一个集合里面。
那如果我们想把两个不同的集合合成一个集合怎么办呢?
很简单,只要将其中一个集合的代表放在另一个集合之中就可以了
模板如下
首先是初始化
void Make_Set(int n){
for(int i=1;i<=n;i++){
pre[i]=i;
}
}
然后是查集
int Find(int x){
if(pre[x]!=x)
pre[x]=Find(pre[x]);
return pre[x];
}
最后是并集
void Join(int x,int y){
int fx=Find(x),fy=Find(fy);
if(fx!=fy)
pre[fx]=fy;
}
线段树
线段树和下文将提到的树状数组一样,是解决区间问题的数据结构,但线段树可以实现的功能比树状数组多。
因为牵扯的东西有点多,所以就又开了一篇博客,这里就不展示了。
树状数组
吖,这个东西不好讲
那我们又开一篇博客吧!!!
绝对不是因为我想水博客
DP
线性DP
后面的有时间再写吧。。。