位运算
AcWing 801. 二进制中1的个数
给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。
法一: 使用lowbit 时间复杂度为O(nlogn),使用lowbit操作,每次操作截取一个数字的最后一个1后面的所有位,每次减去lowbit得到的数字,直到数字减到0,就得到了最终1的个数。
#include <iostream>
using namespace std;
int lowbit(int x)
{
return x&-x;
}
int main()
{
int n;
cin>>n;
for(int i=0; i<n; i++)
{
int x,sum=0;
cin>>x;
while (x)
{
x=x-lowbit(x);
sum++;
}
cout<<sum<<" ";
}
}
法二: 暴力枚举 对于每个数字,我们将其与1相与,就得到了该数字的最后一位,之后将a右移一位,直到为0,就得到了1的个数。
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=0; i<n; i++)
{
int x,sum=0;
cin>>x;
while (x)
{
if(x&1)
{
sum++;
}
x=x>>1;
}
cout<<sum<<" ";
}
}
双指针算法
1.AcWing 799. 最长连续不重复子序列
给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
#include<iostream>
using namespace std;
const int N = 100010;
int q[N],s[N];
int main()
{
int n;
cin >> n;
for (int i = 0; i <n; i ++ )
{
cin >> q[i];
}
int res = 0;
for (int i = 0,j=0; i < n; i ++ )
{
s[q[i]]++;
while(j<i&&s[q[i]]>1)//有重复元素时,j右移直到无重复元素
{
s[q[j]]--;
j++;
}
res = max(res,i-j+1);
}
cout << res << endl;
return 0;
}
声明了两个整型数组q和s,数组q用于存储输入的整数序列,数组s则起到记录元素出现次数的作用,用来辅助判断元素是否重复。
进入main函数后,先是通过cin >> n;语句从标准输入读取一个整数,并将其赋值给变量n,这个n表示输入的整数序列的长度。
然后是一个for循环,循环从i = 0开始,到i < n结束,在每次循环中,通过cin >> q[i];语句读取一个整数,并将其存储到数组q对应的q[i]位置中,如此便完成了整个整数序列的输入操作。
接着初始化一个变量res为0,这个变量是用于记录最终的结果,也就是满足特定条件的子序列的最大长度。
之后进入一个关键的for循环,这里通过两个指针i和j来对数组q进行遍历操作,其中i从0开始逐步往后移动,j也从0开始,并且会随着循环中的情况动态往后移动。在每次循环中,首先执行s[q[i]]++;,这表示数组q中当前位置i对应的元素q[i]在数组s中的出现次数加1,也就是记录元素q[i]出现的频次。
随后进入一个while循环,其判断条件是j < i && s[q[i]] > 1,这个条件意味着当指针j小于指针i(保证窗口合理移动,不会出现不合理回溯等情况)并且当前元素q[i]在数组s中的出现次数大于1(即出现重复元素了)时,就需要对窗口进行调整。此时执行s[q[j]]--;,也就是把窗口最左边(由指针j指向)的元素q[j]在数组s中的出现次数减1,表示这个元素在当前考虑的子序列中的出现次数减少了,然后执行j++;,将指针j往后移动一位,相当于缩小窗口,去除掉重复的元素,使得当前考虑的子序列中每个元素最多只出现一次。
在每次调整完窗口后,通过res = max(res, i - j + 1);来更新res的值,这里的i - j + 1计算的是当前窗口(也就是当前满足条件的子序列)的长度,然后使用max函数将它与之前记录的最大长度res进行比较,把较大的值赋给res,这样就能始终记录下满足条件(即子序列中每个元素最多出现一次)的子序列的最大长度。
最后,通过cout << res << endl;输出最终记录的最大长度值,然后main函数通过return 0;语句结束,表明程序正常执行完毕。
2.AcWing 800. 数组元素的目标和
给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。
数组下标从 0 开始。
请你求出满足 A[i]+B[j]=x 的数对 (i,j)。
数据保证有唯一解。
#include <iostream>
using namespace std;
const int N = 100010;
int a[N], b[N];
int main()
{
int n, m, x;
cin >> 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]);
int i = 0, j = m - 1;
for (; i < n; i ++)// 第一个数组依次遍历
{
while (a[i] + b[j] > x && j > 0){//当大于x是下一个i相加仍大于不用再管后面的j
j --;
}
if(a[i] + b[j] < x) continue;// 此时i,找不到对应的j
if(a[i] + b[j] == x) break;// 找到后退出
}
printf("%d %d",i, j);
return 0;
}
使用两个 for 循环分别接收用户输入的数组 a 和 b 的各个元素,将它们依次存储到对应的数组中。
接下来进入核心的查找逻辑部分。初始化两个指针变量 i 为 0,j 为 m - 1(也就是让 j 指向数组 b 的最后一个元素)。随后进入一个外层的 for 循环,这个循环以 i 遍历数组 a,在每次循环中,会有一个内层的 while 循环,它会不断判断当前 a[i] 与 b[j] 的和是否大于 x,如果大于且 j 大于 0(保证不越界),就将 j 的值减 1,也就是让 b 数组的指针往前移动,尝试找到更小的 b 数组中的元素,使得和有可能等于 x。
之后通过 if 语句进行判断,如果 a[i] + b[j] 的和小于 x,那就执行 continue,直接进入下一次外层 for 循环,去尝试下一个 a 数组中的元素;而如果恰好 a[i] + b[j] 的和等于 x,就执行 break 跳出外层的 for 循环,意味着找到了满足条件的元素组合。
最后,通过 cout 将此时的 i 和 j 的值输出,也就是输出找到的满足条件的两个数组元素对应的索引(这里需要注意,如果没有找到满足条件的元素组合,直接输出此时的 i 和 j 可能不符合预期,原代码在这方面缺乏相应的错误处理机制),最后 main 函数返回 0,表示程序正常结束。
1267

被折叠的 条评论
为什么被折叠?



