2023寒假训练week3

Day1

2023牛客寒假算法基础集训营4

A.清楚姐姐学信息论

1.看懂题目,看出规律
2.求导

#include<iostream>
using namespace std;
int main()
{
long long x,y;
cin>>x>>y;
if(x==3||y==3)cout<<3; // 3的时候效率最大
else cout<<min(x,y);
}

L.清楚姐姐的三角形I

给定三个数,两两相加已知。判断他们能否组成三角形

#include<bits/stdc++.h>
using namespace std;
signed main()
{
int t;
cin>>t;
while(t--)
{
int a,b,c;
cin>>a>>b>>c;
int sum = (a+b+c)/2;
int la = ((b+c) - a)/2; //a的长度
int lb = ((a+c) - b)/2;
int lc = ((a+b) - c)/2;
if (la <= 0 || lb <= 0 || lc <= 0 || ((b+c) - a) % 2!=0) {//不能是奇数,每条边不能小于0
cout << "No"<<endl;
}else if(la + lb <= lc || la + lc <= lb || lb + lc <= la){//两边之和大于第三边
cout<<"No"<<endl;
}else if(abs(la - lb) >= lc || abs(la - lc) >= lb || abs(lb - lc) >= la){//三角形两边之差小于第三边
cout<<"No"<<endl;
}else{
cout<<"Yes"<<"\n"<<la<<" "<<lb<<" "<<lc<<endl;
}
}
}

M.清楚姐姐的三角形II

1.相邻三项一定不能组成三角形
2.只要满足两边之和等于第三边就可以
循环输出112显然不错

#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,i;
cin>>n;
long long a[n];
a[0]=1;
a[1]=1;
a[2]=2;
for(i=0;i<n;i++)
{
a[i]=a[i%3];
cout<<a[i]<<" ";
}
return 0;
}

每日一题【模板】并查集

完成合并和查询操作

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int n,m,z;
int fa[N];
int find_(int x)//返回x的祖宗结点
{
if(fa[x]!=x) fa[x]=find_(fa[x]);//如果x不等于祖宗结点
return fa[x];//让他找到x的根节点,并让x等于他的根结点
}
void union_(int x,int y)
{
fa[find_(x)]=find_(y);//让x的祖宗结点等于y的祖宗结点
}
int main()
{
cin>>n>>m;
//初始化
for(int i=0;i<n;i++) fa[i]=i;
int x,y;
for(int i=0;i<m;i++)
{
cin>>z;
cin>>x>>y;
if(z==1){
union_(x,y);
}
else{
if(find_(x)==find_(y)){
cout<<"Y"<<endl;
}
else{
cout<<"N"<<endl;
}
}
}
return 0;
}

Day2

洛谷P1551 亲戚(并查集)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int n,m,p;
int fa[N];
int find(int x)//返回x的祖宗结点
{
if(fa[x]!=x) fa[x]=find(fa[x]);//如果x不等于祖宗结点
return fa[x];//让他找到x的根节点,并让x等于他的根结点
}
void union_(int x,int y)
{
fa[find(x)]=find(y);//让x的祖宗结点等于y的祖宗结点
}
int main()
{
cin>>n>>m>>p;
for(int i=0;i<n;i++) fa[i]=i;
while(m--)
{
int m1,m2;
cin>>m1>>m2;
union_(m1,m2);
}
while(p--)
{
int p1,p2;
cin>>p1>>p2;
if(find(p1)==find(p2))
{
cout<<"Yes"<<endl;
}
else
{
cout<<"No"<<endl;
}
}
return 0;
}

洛谷P1196 [NOI2002] 银河英雄传说

ACwing数组元素的目标和(双指针)

找出两个升序数组,满足a[i]+b[j]=x的数对(i, j)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+100;
LL a[N],b[N];
int main()
{
int n,m,x;
cin>>n>>m>>x;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<m;i++) cin>>b[i];
//均升序
for(int i=0,j=m-1;i<n;i++){
while(j>=0&&a[i]+b[j]>x) j--;
if(a[i]+b[j]==x){
cout<<i<<" "<<j<<endl;
break;
}
}
return 0;
}

ACwing判断子序列(双指针)

请你判断a序列是否为b序列的子序列

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<m;i++) cin>>b[i];
int i=0,j=0;
while(i<n&&j<m){
if(a[i]==b[j]) i++; //匹配到一组,i++到下一个数
j++; //不管有没有匹配,j都要到下一个数
}
if(i==n) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}

ACwing最长连续不重复子序列(双指针)

一段长度为n的序列,要求找出最长的不重复子序列

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int s[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int res=0;
for(int i=0,j=0;i<n;i++)
{
s[a[i]]++; //当a[i]>1就说明a[i]不只出现1次,有重复
while(s[a[i]]>1)
{
s[a[j]]--;
j++; //j从左开始,依次弹出a[j],直到重复元素a[i]。
}
res=max(res,i-j+1);
}
cout<<res<<endl;
return 0;
}

ACwing区间和(离散化、前缀和)

1.离散化存储每个数据
2.前缀和求区间和

#include<bits/stdc++.h>
using namespace std;
const int N = 300010; //n次插入和m次查询相关数据量的上界
int n, m;
int a[N];//存储坐标插入的值
int s[N];//存储数组a的前缀和
vector<int> alls; //存储(所有与插入和查询有关的)坐标
vector<pair<int, int>> add, query; //存储插入和询问操作的数据
int find(int x) { //返回的是输入的坐标的离散化下标
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 l;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int x, c;
scanf("%d%d", &x, &c);
add.push_back({x, c});
alls.push_back(x);
}
for (int i = 1; i <= m; i++) {
int l , r;
scanf("%d%d", &l, &r);
query.push_back({l, r});
alls.push_back(l);
alls.push_back(r);
}
//排序,去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
//执行前n次插入操作
for (auto item : add) {
int x = find(item.first);
a[x] += item.second;
}
//前缀和
for (int i = 1; i <= alls.size(); i++) s[i] = s[i-1] + a[i];
//处理后m次询问操作
for (auto item : query) {
int l = find(item.first);
int r = find(item.second);
printf("%d\n", s[r] - s[l-1]);
}
return 0;
}

ACwing数n的二进制表示中n的第k位是几

  1. 将n右移k位(将n的第k位移到最后一位)
    n>>k ;
  2. n的最后一位是几
    n & 1 ;

10的二进制表示

#include<bits/stdc++.h>
using namespace std;
int main()
{
int n=10;
for(int k=3;k>=0;k--) cout<<(n<<k)&1;
return 0;
}

二进制表示中n的第k位是几

#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
int k;
cin>>K;
cout<<(n<<k)&1;
}

ACwing二进制中1的个数

lowbit(x)操作返回x的最后一位1

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long LL;
int lowbit(int x)
{
return x&-x;
}
int main()
{
int n;
cin>>n;
while(n--){
int x;
cin>>x;
int res=0;
while(x){
x-=lowbit(x); //x-x的最后一位1直到把1都减无
res++;
}
cout<<res<<" ";
}
return 0;
}

ACwing区间合并

1.要求合并所有有交集的区间

2.输出合并完成后的区间个数

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int>PII;
vector<PII>segs;
int n;
void merge(vector<PII> &segs){
vector<PII> res;
// 左端点排序
sort(segs.begin(), segs.end());
// 左右端点初始化,-无穷
int start = -2e9, end = -2e9;
for(auto seg: segs){
if(end < seg.first){
// 初始的[-无穷,-无穷]区间要跳过,不能装入
if(start != -2e9) res.push_back({start, end});
start = seg.first, end = seg.second;
}
else end = max(end, seg.second);
}
// 有两个作用,1.是防止n为0,把[-无穷,-无穷]压入;2.是压入最后一个(也就是当前)的区间,若n>=1,if可以不要
if (start != -2e9) res.push_back({start, end});
//覆盖segs
segs = res;
}
int main()
{
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;
return 0;
}

ACwing滑动窗口(单调队列)
1.
确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

2.因为窗口位三个数,后面进来的数一定比前面进来的数后出。因此以最大值为例,若后一个

入队的数比前一个入队的数大,则前面的数永远没有出头之日,要被弹出队列。
双端队列版本:

双端队列相当于加强版vector,支持前后端弹出缺点就是比较慢

使用它主要是因为有clear()操作

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i ++ ) cin >> a[i];//读入数据
deque<int> q;
for(int i = 1; i <= n; i++)
{
while(!q.empty()&& q.back() > a[i]) //新进入窗口的值小于队尾元素,则队尾出队列
q.pop_back();
q.push_back(a[i]);//将新进入的元素入队
if(i - k >= 1 && q.front() == a[i - k])//若队头是否滑出了窗口,队头出队
q.pop_front();
if(i >= k)//当窗口形成,输出队头对应的值
cout << q.front() <<" ";
}
q.clear();
cout << endl;
//最大值亦然
for(int i = 1; i <= n; i++)
{
while(!q.empty()&& q.back() < a[i]) q.pop_back();
q.push_back(a[i]);
if(i - k >= 1 && a[i - k] == q.front()) q.pop_front();
if(i >= k) cout << q.front() << " ";
}
}

Day3

SMU Winter 2023 Round #9 (Div.2)

A. Who is The 19th ZUCCPC Champion

随便输出一个字符串就行。

#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"Ranni the Witch\n";
return 0;
}

B. Jiubei and Overwatch

  1. t<=k,damage=tx;
  2. t>k,damage=kx+(t-k)y;
    当时间小于k的时候,就是t倍的x。超过k的话,超过多少就加多少倍的y。
    排序找出最大的防御力的怪物,因为同时攻击,最大的灭了,其他小的自然就灭了。
    分成两部分求解:
  3. 没超过k
  4. 超过k
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n,k,x,y;
int a[N];
void sovle()
{
cin>>n>>k>>x>>y;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n,greater<int>());
// cout<<a[0]<<endl;
for(int t=1;;t++){
if(t<=k)
{
int dam=t*x;
if(a[0]<=dam)
{
cout<<t<<endl;
break;
}
}
else{
int dam=k*x+(t-k)*y;
if(a[0]<=dam)
{
cout<<t<<endl;
break;
}
}
}
}
int main()
{
int _;
cin>>_;
while(_--){
sovle();
}
}

C. Ah, It's Yesterday Once More

交换排序:

for(i->n)
for(j->n)
if(ai<aj) swap(ai,aj);

冒泡排序:

for(i->n)
for(j->n-1)
if(aj>a(j+1)) swap(ai,a(j+1));

题目要求找出全排列数组a,使得冒泡排序和交换排序的交换次数相同。
显然倒序输出的时候相同。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
void sovle()
{
int n;
cin>>n;
for(int i=n;i>0;i--)
{
cout<<i<<" ";
}
cout<<endl;
}
int main()
{
int _;
cin>>_;
while(_--){
sovle();
}
}

F. Sum of Numerators

将分数化简到最小的分子相加。
1.奇数时:分子直接相加
2.偶数时:分子不断除2
3.找规律

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll t;
ll n,k;
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&k);
ll sum=n*(n+1)/2; //等差数列前n项和,未经处理的分子的和。
while(n&&k){
n/=2; //会被约分的数字个数每次都是总长的一半
k--; //每次被约分,2的次数减一
sum-=n*(n+1)/2; //每次被约分掉的总和是等差数列求1到被约分掉的数字个数之和。
}
printf("%lld\n",sum);
}
return 0;
}

L. Monster Tower

塔高为n,每层都有一个怪物,怪物能量为ai,找出初始的攻击力x最小可以为多少。
1.每次只能在1~k层攻击怪物,每次消灭掉怪物i之后,自身的攻击力将会提升ai
2.消灭掉这一层怪物后,这一层会消失,这层往上的层数会逐层-1
思路:
打怪的顺序是唯一的,所以维护一个有 k 个元素的小根堆,每次处理最小的元素,如果现有能力值大于怪的能力值,加上怪的能力值,否则更新初始值;

每遇到一个打不过的怪,最小可能初始值 = 怪的能力值-前面获得的能力值总和,用这个值去更新之前的 初始值,取其中大的;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+100;
ll t,n,k;
ll a[N];
priority_queue<ll,vector<int>,greater<int> >pmin;
ll sta,now;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>k;
sta=0;now=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)//每个堆里只有k个元素
{
if(pmin.empty()||pmin.size()<k) pmin.push(a[i]);//注意判空
else
{
ll s=pmin.top();
pmin.pop();
if(now>=s)
{
now+=s;
}//总能力值大于怪的能力值
else
{
sta=max(sta,s-now);
now+=s;
}//更新初始值,注意更新方式
pmin.push(a[i]);
}
}
while(!pmin.empty())//清空堆
{
ll s=pmin.top();
pmin.pop();
if(now>=s)
{
now+=s;
}
else
{
sta=max(sta,s-now);
now+=s;
}
}
cout<<sta<<endl;
}
return 0;
}

Day4

洛谷P1111 修复公路(并查集)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
int fa[N],num,ans;
struct node{
int x,y,t;
}e[N];
bool operator<(node a,node b){//重载小于运算符
return a.t<b.t;
}
int find(int x)
{
if(fa[x]!=x)
{
fa[x]=find(fa[x]);
}
return fa[x];
}
void union_(int x,int y,int t)
{
if(find(x)==find(y));
else{
fa[find(x)]=find(y);
num++;
ans=max(ans,t);
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
cin>>e[i].x>>e[i].y>>e[i].t;
}
sort(e+1,e+m+1);//按照时间排序
for(int i=1;i<=m;i++) union_(e[i].x,e[i].y,e[i].t);//按时间大小合并
if(num!=n-1) cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
}

leetcode242.有效字母异位词(数组哈希)

class Solution {
public:
bool isAnagram(string s, string t) {
if(s.size()!=t.size())return 0;
int res[26];
for(int i=0;i<26;i++) res[i]=0;
for(int i=0;i<s.size();i++){
res[s[i]-'a']++;
}
for(int i=0;i<t.size();i++){
res[t[i]-'a']--;
}
for(int i=0;i<26;i++){
if(res[i]){
return 0;
}
}
return 1;
}
};

leetcode349.两数组的交集(set哈希)

1.用unodered_set模拟hash处理

2.用数组模拟hash处理

法1:

class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int>result;//存放交集
unordered_set<int> num_set(nums1.begin(),nums1.end());//直接将num1转化为unoderde_set类型
for(int num : nums2)//看num2里的元素是否在num1中出现
{
if(num_set.find(num)!=num_set.end())//如果找不到对应元素,返回的是末尾的迭代器。
{
result.insert(num);
}
}
return vector<int>(result.begin(),result.end());
}
};

法2:

class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int>result;//存放交集
int hash[1005]={0};//hash数组,初始化为0
for(int num : nums1)
{
hash[num]=1;
}
for(int num : nums2)
{
if(hash[num]){
result.insert(num);//nums2的元素出现过,直接插入result中
}
}
return vector<int>(result.begin(),result.end());
}
};

leetcode1.两数之和(map哈希)

找出和为目标值target的那两个整数,并返回它们的数组下标。
num1+num2=target
1.key值存放num2,value存放下标。

2.遍历num1,看看map中是否存储了num2,防止重复计算。

class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map <int,int> map;//无序,first存储数值,second存储下标
for(int i=0;i<nums.size();i++){//查询map中是否有相加等于target的元素
auto it=map.find(target-nums[i]);
if(it!=map.end())//map中,找得到和为target的另一个数的下标
return {it->second,i};
// 如果没找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int,int>{nums[i],i});
}
return {};
}
};

leetcode454.四数相加(II)(map哈希)

给定四个数组,求num1[i]+num2[j]+num3[k]+num4[l]=target,一共有几种方法

1.朴素写法

class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int>map;
for(int i=0;i<nums1.size();i++){//将nums1+nums2的值存在map中
for(int j=0;j<nums2.size();j++){
map[nums1[i]+nums2[j]]++;
}
}
int conut=0;
for(int i=0;i<nums3.size();i++){
for(int j=0;j<nums4.size();j++){
auto it=-(nums3[i]+nums4[j]);
if(map.find(it)!=map.end()){//找到了
conut+=map[it];
}
}
}
return conut;
}
};

2.优化

class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int>map;
for(int i : nums1){//将nums1+nums2的值存在map中
for(int j : nums2){
map[i+j]++;
}
}
int conut=0;
for(int i : nums3){
for(int j : nums4){
auto it=-(i+j);
if(map.find(it)!=map.end()){//找到了
conut+=map[it];
}
}
}
return conut;
}
};

leetcode15.三数之和(双指针)

1.给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c 。使得 a

+b+c=0请你找出所有满足条件且不重复的三元组。

2.答案中不可以包含重复的三元组。

详细介绍:
例如答案为:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
这题的麻烦主要时去重

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());//排序
vector<vector<int>> result;
for(int i=0;i<nums.size();i++){
if(nums[i]>0) return result;//最小的数大于0,说明不管怎相加都不会等于0
if(i>0&&nums[i]==nums[i-1])//去重复
continue;
int l=i+1;
int r=nums.size()-1;
while(l<r){
if(nums[i]+nums[l]+nums[r]>0){
r--;
}
else if(nums[i]+nums[l]+nums[r]<0){
l++;
}
else{
result.push_back(vector<int>{nums[i] ,nums[l] ,nums[r]});
//接下来要对l和r去重
while(l<r&&nums[l]==nums[l+1]) l++;
while(l<r&&nums[r]==nums[r-1]) r--;
//找到result后,将l和r收缩
l++;
r--;
}
}
}
return result;
}
};

leetcode18.四数之和(双指针)

1.剪枝
2.去重
3.三数之和

class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
for(int a=0;a<nums.size();a++){
if(nums[a]>0&&target>0&&nums[a]>target){//剪枝
break;
}
if(a>0&&nums[a]==nums[a-1]){//对a去重
continue;
}
for(int b=a+1;b<nums.size();b++){//剪枝
if(nums[b]+nums[a]>0&&target>0&&nums[a]+nums[b]>target){
break;
}
if(b>a+1&&nums[b]==nums[b-1]){//对b去重
continue;
}
int c=b+1,d=nums.size()-1;
while(c<d){
// nums[a] + nums[b] + nums[c] + nums[d] > target 会溢出
if((long)nums[a]+nums[b]+nums[c]+nums[d]>target) d--;
else if((long)nums[a]+nums[b]+nums[c]+nums[d]<target) c++;
else{
result.push_back(vector<int>({nums[a],nums[b],nums[c],nums[d]}));
//获得答案之后对c和d进行去重
while(c<d&&nums[c]==nums[c+1]) c++;
while(c<d&&nums[d]==nums[d-1]) d--;
//获得答案后将范围收缩
c++;
d--;
}
}
}
}
return result;
}
};

leetcode344.反转字符串(I)(双指针)

要求空间复杂度o(1)即不能另外开一个字符串,而改变s字符串的值

class Solution {
public:
void reverseString(vector<char>& s) {
char t;
for(int i=0,j=s.size()-1;i<j;i++,j--){
t=s[j];
s[j]=s[i];
s[i]=t;
}
}
};

leetcode541.反转字符串(II)(双指针)

给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。

如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例:

输入: s = "abcdefg", k = 2
输出: "bacdfeg"

1.可以自己写一个反转函数

class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=(2*k)){ // 以2k间距来操作
if(i+k<=s.size()){ //i后面字符大于k个,反转k个字符
for(int j=i,l=i+k-1;j<l;j++,l--){//注意在[i,i+k)区间内反转
char t=s[j];
s[j]=s[l];
s[l]=t;
}
continue;
}
else{ //到最后,i后面的字符小于k个
for(int j=i,l=s.size()-1;j<l;j++,l--){
char t=s[j];
s[j]=s[l];
s[l]=t;
}
}
}
return s;
}
};

2.可以用stl函数

class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=(2*k)){ // 以2k间距来操作
if(i+k<=s.size()){ //i后面字符大于k个,反转k个字符
reverse(s.begin() + i, s.begin() + i + k );
continue;
}
else{ //到最后,i后面的字符小于k个
reverse(s.begin() + i, s.end());
}
}
return s;
}
};

Day6

洛谷P1536 村村通(并查集)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int n,m;
int fa[N];
int find(int x)//返回x的祖宗结点
{
if(fa[x]!=x) fa[x]=find(fa[x]);//如果x不等于祖宗结点
return fa[x];//让他找到x的根节点,并让x等于他的根结点
}
void union_(int x,int y)
{
fa[find(x)]=find(y);//让x的祖宗结点等于y的祖宗结点
}
int main()
{
while(1)
{
cin>>n;
if(n==0) return 0;
cin>>m;
for(int i=1;i<=n;i++) fa[i]=i;//城镇
while(m--)
{
int m1,m2;
cin>>m1>>m2;//由这条道路相连的城镇编号
union_(m1,m2);
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(find(i)==i)
{
ans++;
}
}
cout<<ans-1<<endl;
}
return 0;
}

leetcode27. 移除元素(双指针)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow=0;
for(int fast=0;fast<nums.size();fast++){
if(nums[fast]!=val){ //快指针的值不等于val时将元素的值赋给nums[slow]
nums[slow]=nums[fast];
slow++; //慢指针移动
}
}
return slow;
}
};

leetcode151. 反转字符串中的单词(双指针)

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:
输入: "the sky is blue"
输出: "blue is sky the"

示例 2:
输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:
输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
一定要记得resize操作

class Solution {
public:
string reverseWords(string s) {
//空格移除操作
int slow=0;//慢指针
for(int fast=0;fast<s.size();fast++){//快指针
if(s[fast]!=' ')//如果fast指向的元素不为空格
{
if(slow!=0)//如果slow指向的元素不为首元
{
s[slow]=' ';//就在单词前加空格
slow++;
}
while(fast<s.size()&&s[fast]!=' ')//当在数组内,且不等于空格时
{
s[slow]=s[fast];//覆盖
slow++;
fast++;
}
}
}
s.resize(slow); //slow的大小即为去除多余空格后的大小。
//反转操作,先反转整个句子,再逐个反转单词
for(int i=0,j=s.size()-1;i<j;i++,j--)
{
char t=s[i];
s[i]=s[j];
s[j]=t;
}
int st=0;//起始的单词字母在0处
for(int i=0;i<=s.size();i++){
if(s[i]==' '||i==s.size()){ //到达单词结尾
for(int l=st,r=i-1;l<r;l++,r--){
char t=s[l];
s[l]=s[r];
s[r]=t;
}
st=i+1; //新单词的起始位置
}
}
return s;
}
};
posted @   Is_Qinna  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示