双指针算法
示例:输出字符串 ,如
输入abc dfsf sasa
输出 abc
dfsf
sasa
结构:
for(int i=0;i<n;i++){
int j=i;
while(check(j))j++;
+每道题的具体逻辑
}
先写出本题暴力解法i是数组区间尾指针,j是数组区间头指针遍历每一个区间找出最大值,优化解法是对于每一个末尾指针,找出距离他最远的头指针j,用一个S[N]作为计数数组,记录a[n]中元素出现的次数
#include <iostream>
using namespace std;
const int N =1e5+10;
int a[N],s[N];//输入序列,计数序列
int n;
int main(){
cin>>n;
int res =0;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
while(s[a[i]]>1){ //如果区间里有重复的数,让j开始遍历,直到区间中没有重复的数 此处不用加上j<=i条件,因为j不可能>i
s[a[j]]--;
j++;
}
res=max(res, i - j +1);
}
cout<<res;
return 0;
}
双指针算法示例:
找到两个数组中存在的线性关系
第一步:使i指向a的第一个数
j指向b的最后一个数
第二步:使j向开头前进
满足条件j>=0&&a[i]+b[j]>x
这时j指向的数使a[i]+b[j]大于x且最接近x
第三步:判断如果找到x,跳出
否则使i++,重复上面的步骤
#include <iostream>
using namespace std;
const int N =1e5+10;
int n,m,x;
int a[N],b[N];
int main(){
scanf("%d%d%d",&n,&m,&x);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<m;i++) scanf("%d",&b[i]);
for(int i=0,j=m-1;i<n;i++)//使指针i从头开始,j从尾开始
{
while(j>=0 && a[i]+b[j]>x)j--;//找到最逼近x的最大的j,并使j--
if(a[i]+b[j]==x){ //如果此时满足条件,输出并跳出,否则i++
printf("%d %d\n",i,j);
break;
}
}
return 0;
}
//这道题想了蛮久,最后已经很接近了,但差了一步,我想的是i,j均从头开始
j向后逼近最接近x且小于x的值,然后j++,这时作比较,再i++
说一下为什么不行
因为j++之后倘若a[i]+b[j]的值大于x,则i++的值必然也大于x
那么就错过了正确答案
为什么y总的答案可以因为j- -之后的值若小于x
那么它i++之后还可以再涨回来大于x
这样保证不错过正确答案
体会一波23333还是太年轻
位运算
//二进制位运算
基本运算方法
把十进制转化成二进制并输出
操作:
比如拿出第i位
操作是右移>>i-1位并&1
n>>k&1
示例:
自己实现了一手:
void ChangeHexToBinary (int x){
int i=0;
vector<int> res;
while(x>>i){
res.push_back(x>>i&1);
i++;
}
reverse(res.begin(),res.end());
for(int i=0;i<res.size();i++)cout<<res[i];
}
lowbit方法
lowbit(x)
返回x(转化成二进制)的最后一位1及后面的0
如lowbit(101000) return 1000
例题:
#include <iostream>
using namespace std;
const int N =1e5+10;
int q[N];
int n;
int lowbit(int x){
return x&-x;//lowbit操作返回最后一位1
}
int main(){
cin>>n;
while(n--){
int x;
cin>>x;
int res =0;
while(x)x-=lowbit(x),res++;//如果x不是零,减掉最后一位1,使计数++ 如1010-10=1000;
printf("%d ",res);
}
return 0;
离散化
//将排好序的数据(从小到大)
一次映射到另外一个数组当中(对应下角标)
并在该数组中完成原来的增删操作
#include <iostream>
#include<vector>
#include <algorithm>
using namespace std;
const int N =300010;表示所有要操作数的范围(n+2m次询问(x,l,r个数总和),最多3*1e5次)
typedef pair <int, int> PII; 表示增加,查询操作,,,每次操作需要两个数据
int n,m;
int a[N],s[N];离散化后数据存放的数组,对应的前缀和数组
vector<int> alls;需要进行离散化的数据
vector <PII> add,query;
int find(int x){ 用二分对alls里的数据进行离散化
int l=0,r=alls.size()-1;
while(l<r){
int mid=l+r>>1;
if(alls[mid]>=x)r=mid;
else l=mid+1;
}
return r+1; 返回的坐标应该从1开始,便于求前缀和操作
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
int x,c;
cin>>x>>c;
add.push_back({x,c});将增加操作对加入到add数组里去
alls.push_back(x); 将需要离散化的x的坐标传到alls数组里面去
}
for(int i=0;i<m;i++){
int l,r;
cin>>l>>r;
query.push_back({l,r});将查找操作对加到query数组里面去
alls.push_back(l);l,r均需要进行离散化,将他们的坐标加到alls数组里面去,此时alls最多装载了30w个数
alls.push_back(r);
}
对alls数组进行去重 为什么去重:
/每个数据只对应离散化数组a[N]中的一个坐标,不能多次插入,只需要得到要用的数组下标就行了
sort(alls.begin(),alls.end()); 去重前先排序,为unique去重函数的使用要求
alls.erase(unique(alls.begin(),alls.end()),alls.end()); erase去除掉unique去重后的多余部分
处理插入操作
for(auto item : add){ 找到x在a[n]中的位置,并完成+c操作
int x=find(item.first);
a[x]+=item.second;
}
for(int i=1;i<=alls.size();i++)s[i]=s[i-1]+a[i];根据a[n]得到前缀和数组s[n]
处理查询操作
for(auto item:query){ 得到l,r在在离散化数组里面对应的位置
int l=find(item.first);
int r=find(item.second);
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
比较长,,,理解,背过!
区间和并
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
vector<PII> segs; /用来存储线段(区间)的向量数组
void merge(vector <PII> &segs){
vector<PII> res; /存放更新区间的向量数组
sort(segs.begin(),segs.end()); /先对区间排序,使得从最左端开始维护
int st=-2e9, ed=-2e9; /设置初始维护区间
for(auto seg:segs){
if(ed<seg.first){
if(ed!=-2e9)res.push_back({st,ed}); /如果满足末尾<seg.first 并且判断的不是初始维护区间,就把该段加入到res里面去
st=seg.first ,ed=seg.second; /更新维护区间
}
else ed=max(ed,seg.second); /如果有重叠的部分就更新区间末尾
}
if(ed!=-2e9)res.push_back({st,ed}); /把最后剩的区间加入进去,if条件防止segs的输入为空
segs=res;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int l,r;
cin>>l>>r;
segs.push_back({l,r});
}
merge(segs);
cout<<segs.size()<<endl;/输出segs里元素(区间)的个数
return 0;
}