ACwing:算法基础课 第一章刷题错题

第一章易错的知识点:

① 一维和二维前缀和的模板都是减去-1的
一维:a[l] + … + a[r] = S[r] - S[l - 1] 是S[l-1]不是S[l]这是因为存储是从1开始存的,所以S[l]包括了编辑的a[i]
二维:以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1],也是同理,

此外,二维的
s[i, j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]

② 离散化的模板,alls存的是待离散化的坐标,把它离散成从1开始的连续自然数
——————————————————————————————————————————————————————————————————
1.错题一:数的三次方根

(1) 原题链接:https://www.acwing.com/problem/content/792/

(2)出错原因:
①check函数不知道怎么比。注意,浮点数的模板是和找左边界的类似的,因此是在左边界的右侧的满足条件,即q[mid]>=x。
②else 更新的是l=mid,不是l=mid+1
③注意while的条件是右边减去左边
④精度要比题目要求的还要多一位小数点,因为多多益善,最后只需要保留题目中所需要的位数即可

(3)代码如下:

#include<iostream>
#include<iomanip>
using namespace std;

double check(double mid){
    return mid*mid*mid;
}

int main(){
    double n;cin>>n;
    double esp=1e-7;
    double l=-10000,r=10000;
    while(r-l>=esp){
        double mid=(l+r)/2;
        if(check(mid)>=n) r=mid;
        else l=mid;
    }
    //cout<<fixed<<setprecision(6)<<l;
    printf("%.6f",l);
}

2.错题二:数的范围

(1) 原题链接:
https://www.acwing.com/problem/content/791/

(2)出错原因:

①注意在找起始与结束位置的时候,其实就是二分找左边界与右边界
②注意在找到左边界的时候要判断是否找的到这位数,如果找不到的话就直接输出结果,否则才继续进行右边界的寻找
③找左边界与右边界的模板不要背混

(3)代码:

{
    int n,k; scanf("%d %d",&n,&k);
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    while(k--){
        int x;scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r){
            int mid=l+r>>1;
            if(q[mid]>=x) r=mid;
            else l=mid+1;
        }
        if(q[l]!=x) cout<<"-1 -1"<<endl;
        else{
            cout<<l<<" ";
            int l=0,r=n-1;
            while(l<r){
                int mid=l+r+1>>1;
                if(q[mid]<=x) l=mid;
                else r=mid-1;
            }
            cout<<l<<endl;
        }
    }
    
    
}

3.错题三:逆序对的数量

(1)原题链接:

https://www.acwing.com/problem/content/790/

(2)出错原因:

①没有很好的分析出题目的规律:注意用归并的时候,我们发现只需要找出左半边的临界点,即这个点大于该此时右半边的点,那么从左半边临界点之后的点都是逆序对,所以得出公式:对于左半边每一个点Sj,它的逆序对有mid-i+1
在这里插入图片描述
②要学会怎么分析数据会不会越界,是用long,long,因为数列最长有n,假设为整个数列都是逆序的,则逆序对有n+n-1+n-2+…+1,即共有n*(n-1)/2,由于n最大为100000,所以最大的逆序对个数可以超过10的9次方,所以用 Long long

(3)代码如下:

#include <iostream>
using namespace std;
const int N=100010;
long long q[N];
long long count=0;
long long temp[N],Orderq[N];

void merge_sort(long long q[],long long l,long long r){
    if(l>=r) return;
    long long mid=l+r>>1;
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    
    long long k=0,i=l,j=mid+1;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else{
            count=count+mid-i+1;
            temp[k++]=q[j++];
        } 
        
    }
    
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(long long i=0,j=l;j<=r;j++,i++) q[j]=temp[i];
}

int main(){
    long long n; scanf("%lld",&n);
    for(long long i=0;i<n;i++) scanf("%lld",&q[i]);
    for(long long i=0;i<n;i++) Orderq[i]=q[i];
    merge_sort(Orderq,0,n-1);
    printf("%lld",count);
}

4.错题四:高精度减法

(1) 原题链接:
https://www.acwing.com/problem/content/description/794/

(2)出错原因:

①两个大整数大小判断的方法错误。因为做减法的时候,要比较两个数的大小,如果小的在前面的话,要调转返回并且输出负号的。一开始我是直接想通过两个vector的数组的规格来比较谁大谁小的,但是很显然是忘记了长度相同的情况,因此判断两个大整数的大小的时候,可以先考虑长度是否相同,如果不相同就返回 return A.size()<B.size(),学会这个表达;如果长度相同,就从后往前遍历(即从大到小,这个我当时也错了),当遇到不相同的元素的时候,就返回 return A[i]<B[i]. 其它情况就是,还别忽略,就是当A和B完全相等的时候,就要返回false,不进行掉转调用函数****(这个我当初也忽略了,所以导致当遇到AB完全相同时,进行一个死循环)

int compared(vector<int>&A,vector<int>&B)
{
    if(A.size()!=B.size()) return A.size()<B.size();
    for(int i=A.size()-1;i>=0;i--) 
    {
        if(A[i]!=B[i]) return A[i]<B[i];
    }
    return 0;
}

②忘记去除结果的前导零了。

③忽略了减法,如果A比B小,最后结果要输出一个负号

(3) 代码如下:

#include<iostream>
#include "string.h"
#include <vector>
using namespace std;

vector<int> C;

bool cmp(vector<int>&A,vector<int>&B)
{
    if(A.size()!=B.size()) return A.size()<B.size();
    for(int i=A.size()-1;i>=0;i--)
        if(A[i]!=B[i]) return A[i]<B[i];
    return false;**//当A=B时,不进行掉转调用函数**
}


void deletealopy(vector<int> &A,vector<int> &B)
{
    if(cmp(A,B)) return deletealopy(B,A);
    for(int i=0,t=0;i<A.size();i++)
    {
        t=A[i]-t;
        if(i<B.size()) t=t-B[i];
        C.push_back((t+10)%10);
        if(t<0) t=1;
        else t=0;
    }
    **//老是忘记去除前导零**
    while (C.size()>1&&C.back()==0) C.pop_back();
}

int main()
{
    string a,b;cin>>a>>b;
    vector<int> A,B;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    deletealopy(A,B);
    if(cmp(A,B)) printf("-");
    for(int j=C.size()-1;j>=0;j--) cout<<C[j];
}

5.错题五:高精度加法

(1) 原题链接:
https://www.acwing.com/problem/content/795/

(2)出错原因:
我服了,字符串PUSH到Vectord的时候,忘记减去-‘0’了。。。。究其原因还是不熟,而且不是死记硬背,而应该是学会思路。前导零勿忘。

6.错题六:最长不重复子序列

(1)原题链接:

https://www.acwing.com/problem/content/801/

(2)出错原因:
不会利用双指针算法分析这题

(3)题目分析:

首先利用两个指针i和j,i一直往右走,而j也一直往右走,但是不能>i,即j就是为了找出离i左边最远的距离。所以如果遇到了重复元素的时候,j就向前一格,这样就保证了j一直都是指向不重复序列中的开始元素。

在这里插入图片描述
(4)代码如下:

注意:s数组下标表示了原数组的数据,因此它是用来存储原数组的数据的个数的,如果个数大于=2就说明有重复的。

#include <iostream>
using namespace std;

const int N=100010;
int a[N],s[N];

bool check(int i,int j)
{
    if(a[j]!=a[i]) return true;
    else return false;
}

int main()
{
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    int res=0;
    for(int i=0,j=0;i<n;i++)
    {
        s[a[i]]++;
        while(s[a[i]]>1)
        {
            s[a[j]]--;
            j++;
        }
        res=max(res,i-j+1);
    }
    printf("%d",res);
    return 0;
}

7.错题7:双指针算法

(1)原题链接:
https://www.acwing.com/problem/content/802/

(2)出错原因

分析的方法错了,该题目设置的两个指针应该是一个从头开始一个从尾开始,这样的话就有单调性了

(3)利用双指针算法的解题思路:
先列出该题目暴力的解法,然后观察有无单调性,如果有的话就可以利用双指针进行时间复杂度的化简。

(4)该题目的正确解题思路

在这里插入图片描述

(5)代码如下:

#include <iostream>
#include <algorithm>

using namespace std;
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++)
    {
        while(j>=0&&a[i]+b[j]>x) j—-;
        if(a[i]+b[j]==x)
        { 
            printf(%d %d”,i,j);
            break;
        }
    }
    return 0;
} 

8.错题八:离散化

(1)原题链接:

https://www.acwing.com/problem/content/804/

(2)题目分析:

本题目一开始就是想用前缀和去做的,但是后面发现元素并不能用数组的进行表示,因为跨度太大,数轴的区间长度长达2乘10的九次方,所以开不了数组,但是我们发现一共处理的操作和查询的操作范围才是n+2m,大概范围才30万左右,因此很明显要用离散化,将无限的数轴映射到1,2,3__n,然后再利用前缀和进行求解。

因此要熟背了离散化模版,此外,要学会如何定义一个pair元素对。

(3)代码如下:

include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

const int N=300010;

typedef pair<int,int> PII;
vector<int>alls;
vector<PII>adds,query;

int n,m;
int a[N],s[N];

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 r+1;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) 
    {
        int x,c;scanf("%d%d",&x,&c);
        adds.push_back({x,c});//存入需要操作的信息
        alls.push_back(x);//将需要操作的元素存入到数组中,
    }
    for(int i=0;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());
    
    //处理插入
    for(auto item:adds){
        int k=find(item.first);
        a[k]+=item.second;
    }
    
    //预处理前缀和:alls映射到a数组下标是从1开始一直到n的
    for(int i=1;i<=alls.size();i++) s[i]=s[i-1]+a[i];
    
    //处理查询
    for(auto item:query){
        int l=find(item.first);
        int r=find(item.second);
        printf("%d\n",s[r]-s[l-1]);
    }
    
    return 0;
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值