《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.6 two pointers

pat A1085

生词:

题意:

在一个数组中,满足max<=min*p的数组个数的最大值

输入:

1.输入n个数字和p

输出:

满足最大值<=最小值*P的序列中个数最多的个数

解题思路1:

在这里插入图片描述
1.设置n的上界maxn=100010,定义n,p,a[maxn]为全局变量
2.i,j分别表示前后遍历数组a的下标,根据题意可知a[j]<=a[i]*p,并且要求j-i最大
3.利用二分的思想:找出从i+1~n-1这个范围内找出第一个比a[i]*p大的数字的下标j(其实真正满足a[j]<=a[i]*p的数字的下标为j-1,但是最后的结果是要找到最大的长度,即为此时的j-i)
4.设置ans为1,然后每次与二分函数binarySearch(int i, long long x)返回的值进行比较,得出最大的,最后输出

参考代码1:

#include
#include
using namespace std;
const int maxn=100010;
int n,p,a[maxn];
/*
    解题思路:


    题意:
    1.找出满足最大值<=最小值*P的序列中个数最多的个数

*/
//binarySearch函数在[i+1,n-1]范围内查找第一个大于x的数的位置
int binarySearch(int i, long long x)
{
    if(a[n-1]<=x)      //如果所有数都不大于x,返回n
        return n;
    int l=i+1,r=n-1,mid;               //在[i+1,n-1]内查找
    while(l<r)
    {
        mid=(l+r)/2;
        if(a[mid]<=x)   //如果a[mid]<=x,说明第一个大于x的数只可能在mid后面
        {
            l=mid+1;   //左端点即为mid+1
        }
        else
        {
            r=mid;          //右端点即为mid
        }
    }
    return l;     //由于while结束时l==r,因此返回l或者r皆可
}
int main()
{
    cin >> n >> p;
    for(int i=0; i<n; i++)
    {
        cin >> a[i];
    }
    sort(a,a+n);       //递增排序
    int ans=1;        //最大长度,初值为1(表示至少有1个数字)
    for(int i=0; i<n; i++)
    {
        //在a[i+1]-a[n-1]中查找第一个超过a[i]*p的数,返回其位置给j
        int j=binarySearch(i,(long long)a[i]*p);
        ans=max(ans,j-i);     //更新最大长度,最后找的是满足条件的序列的最大长度而非最大数字
    }
    cout << ans << endl;
    return 0;
}

注意事项:

a[maxn]不用设置为long long类型,因为调用函数时,用long long强制转化
在这里插入图片描述

解题思路2:

在这里插入图片描述

参考代码2:

#include
#include
using namespace std;
const int maxn=100010;
int n,p,a[maxn]; //
int main()
{
    cin >> n >> p;
    for(int i=0; i<n; i++)
        cin >> a[i];
    sort(a,a+n);
    int ans=1;
    for(int i=0; i<n; i++)
    {
        int j=upper_bound(a+i+1,a+n,(long long)a[i]*p)-a; //使用upper_bound(a+i,a+j,x)-a返回的是第一个大于x的数的坐标
        ans=max(ans,j-i);
    }
    cout << ans;
    return 0;
}

知识总结:

upper_bound(a+i,a+j,x)-a返回的是第一个大于x的数的坐标

#include
#include
using namespace std;
const int maxn=100010;
int n,p,a[maxn]; //
int main()
{
    int x,i;
    cin >> n >> x;
    for(i=0; i<n; i++)
        cin >> a[i];
    cout << upper_bound(a,a+n,x)-a; //使用

    return 0;
}

在这里插入图片描述

解题思路3:two pointers思想:

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.6 two pointers_第1张图片
解题思路:
1.设置数组,输出,排序
2.设置两个pointers:letf,right都用0开始
3.双层while,一个走left,一个走right
4.如果说有两个值,前面是相同的,中间while找到合适的right后,最后会在实际的right的位置向后移一位,然后
进入下次循环,i向后移一位,如果此时i所指的值和前面相同,则此时的right所表示的数字一定不满足,所以也要
向后移动,开始重新查找

解题思路3:参考代码:

#include
#include
using namespace std;
/*

*/
int main()
{
    int n,p;
    cin >> n >> p;
    int v[100001];
    for(int i=0; i<n; i++)
        cin >> v[i];
    sort(v,v+n);
    int left=0,right=0,maxn=-1;
    while(left<n && right<n)  //设置两个tow pointer:left right
    {
        //注意:在下面的代码中,p的取值范围在 p<=10^9 所以把他转化为long long类型
​​ ) 
        while(right<n && v[right]<=v[left]*p(long long))  //注意这里不要忘记right
        {
            if(maxn<right-left+1)
                maxn=right-left+1;
            right++;
        }
        left++;
    }
    cout << maxn;
    return 0;
}

pat A1089

题意:在这里插入图片描述

输入:

1.输入n,表示n个数字
2.输入n个原始的数字
2.输入n个经过排序的目标数字

输出:

1.如果满足插入排序,则输出Insertion Sort,并且输出下一次它所对应的排序序列
2.如果满足归并排序,则输出Merge Sort,并且输出下一次它所对应的排序序列

解题思路:

在这里插入图片描述

参考代码:

#include
#include
#include
using namespace std;
/*
    整体的思路是:
    1.先进行插入排序,如果执行过程中返现与给定的序列温和,说明则是插入排序然后输出下一个值,然后结束算法
    2.如果不是插入排序,则是归并排序,则模拟归并排序,然后输出下一次的排序结果
*/
const int N=111;
int origin[N],tempOri[N],changed[N];   //原始数组,原始数组备份,目标数组
int n;   //元素个数
bool isSame(int A[],int B[])   //判断数组A和数组B是否相同
{
    for(int i=0; i<n; i++)
    {
        if(A[i]!=B[i])
            return false;
    }
    return true;
}

bool showArray(int A[]) //输出数组,为了保证输出时最后一个没有空格
{
    for(int i=0; i<n; i++)
    {
        printf("%d",A[i]);
        if(i<n-1)
            cout << " ";
    }
    cout << endl;
}

bool insertSort ()  //插入排序
{
    //这个插入排序的思想非常简单
    //1.设置flag为false,对于最开始的数组不进行比较,先进行一次插入排序,此时flag肯定还是false
    //2.然后for循环进行比较,如果目标数组和备份数组相同则flag为true,下面在进行一次排序,然后判断flag,
    //当找到备份数组与目标数组相同时,其实已经经过了一次排序,也就是说在main函数中,直接输出就行
    //

    bool flag=false;  //记录是否存在数组中间步骤与changed数组相同
    for(int i=1; i<n; i++)  //数组下表输入时按照从0开始输入,所以要进行插入排序时必须从i==1开始插入
    {
        if(i!=1 && isSame(tempOri,changed))  //不让输入的初始数组和目标数组进行比较,肯定是经过排序后才开始进行比较
        {
            flag=true;       //如果isSame返回true,那么就肯定定flag为true
        }
        //以下为插入部分
        int temp=tempOri[i],j=i;
        while(j>0 && tempOri[j-1]>temp)
        {
            tempOri[j]=tempOri[j-1];
            j--;
        }
        tempOri[j]=temp;
        if(flag==true)
        {
            return true;   //如果flag为true,则说明以达到目标数组,返回true
        }
    }
    return false;   //无法叨叨目标数组,返回false
}

void mergeSort()  //归并排序
{
    bool flag=false;      //先设置flag为false
    //以下为归并排序部分
    for(int step=2; step/2<=n; step*=2)
    {
        if(step!=2 && isSame(tempOri,changed))   //这样设置的把戏就是为了防止直接将初始数组和目标数组进行比较,肯定是要先进行一次排序才比较
        {
            flag=true;    //中间步骤与目标相同,且不是厨师序列
        }
        for(int i=0; i<n; i+=step)
        {
            sort(tempOri+i,tempOri+min(i+step,n));    //首先在sort函数中,第2个位置写的数一定是要比待排序的数列的最后一位再向后排一位,所以是i+step,然后注意在最后的时候,肯定是只能到数组的末尾
        }
        if(flag==true)   //必然有,第一次的时候肯定flag还是为false,所以不执行此处的代码,但是过了第一次后,如果在前面备份代码和目标代码一致的话,直接flag为true,此时也是已经经过了下一次排序,后面直接输出即可
        {
            showArray(tempOri);  //直接输出对应的数组
            return ;
        }
    }
}
/*
    解题思路:
    1.输入n
    2.输入原始数组和备份数组
    3.输入目标数组
    4.调用insertSort()函数,如果返回值为true,直接输出Insertion Sort
    5.如果不是,则输出Merge Sort,
*/
int main()
{
    cin >> n;
    for(int i=0; i<n; i++)
    {
        cin >> origin[i];  //输入起始数组
        tempOri[i]=origin[i];   //tempori数组为备份,排序过程在tempori上进行
    }
    for(int i=0; i<n; i++)
    {
        cin >> changed[i];   //目标数组
    }
    if(insertSort())
    {
        //如果插入排序中找到目标数组
        cout << "Insertion Sort" << endl;
        showArray(tempOri);
    }
    else
    {
        cout << "Merge Sort" << endl;
        for(int i=0; i<n; i++)
            tempOri[i]=origin[i];   //还原tempori数组,因为前面经过插入排序的数组,将备份数组改的面目全非
        mergeSort();  //归并排序,再调用mergeSort函数
    }

    return 0;
}


pat A1029

题意:

在这里插入图片描述

输入:

1.输入n,后面紧接着输入n个数字
2.输入m, 后面紧接着输入m个数字

输出:

1.将2个数组的数字按照升序的顺序进行输入,然后输出中间位置的数字

解题思路1:

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.6 two pointers_第2张图片

参考代码1:

#include
using namespace std;
/*
    解题思路:
    1.先输入两个数组
    2.设置两个数组,然后将较小的数字往新的数组中放,最后在将剩余的全部放入
    3.按要求输出
*/
int main()
{
    int n,m;
    int s1[20001],s2[20001];
    cin >> n;
    for(int i=0; i<n; i++)
        cin >> s1[i];
    cin >> m;
    for(int j=0; j<m; j++)
        cin >> s2[j];
    int p=0,q=0,news[40007],cos=0;
    while(p<n && q<m)
    {
        if(s1[p]<s2[q])
        {
            news[cos++]=s1[p];
            p++;
        }
        else
        {
            news[cos++]=s2[q];
            q++;
        }
    }
    while(p<n)
        news[cos++]=s1[p++];
    while(q<m)
        news[cos++]=s2[q++];
    if((n+m)%2==0)
        cout << news[(n+m)/2-1];
    else
        cout << news[(n+m)/2];
    return 0;
}

解题思路2:

在这里插入图片描述

参考代码2:

#include
using namespace std;
/*
    解题思路:
    1.先输入两个数组
    2.计算中间位置的下标,然后以中间下标位置为基础开始循环,循环内容是对两个数组的值进行比较,较小的那一个自增,注意中间下标位置的循环变量也要自增
*/
int main()
{
    int n,m;
    int s1[200001],s2[200001];
    cin >> n;
    for(int i=0; i<n; i++)
        cin >> s1[i];
    cin >> m;
    for(int j=0; j<m; j++)
        cin >> s2[j];
    s1[n]=s2[m]=0x7fffffff;    //设就把置最后一个位置数为10^7表示int类型最大的数,这样防止还没有到中间的数,就把其中一个数组访问完了,数组越界的为题
    int p=0,q=0,cos=0,media=(n+m-1)/2;
    while(cos<media)
    {
        if(s1[p]<s2[q])
            p++;
        else
            q++;
        cos++;
    }
    if(s1[p]<s2[q])
        cout << s1[p] << endl;
    else
        cout << s2[q] << endl;
    return 0;
}

知识总结:

1.输入int的上界2^31-1

    a=0x7fffffff
   a=(1<<31)-1;  //1<<31表示2^31

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.6 two pointers_第3张图片
《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.6 two pointers_第4张图片

pat A1048

题意:

输入n个数字和目标数字,如果在所输入的n个数中有和为m的,则输出这俩个数v1,v2 保证v1<=v2 并且 v1是所有满足条件中最小的

输入:

1.输入n
2.输入m,表示目标数字

输出:

1.如果有v1+v2==目标数字,且v1<=v2,且v1是整个满足条件中最小的,则直接输出v1,v2

2.否则输出No Solution

解题思路:

《算法笔记上机实验指南》第4章 入门篇(2)---算法初步 4.6 two pointers_第5张图片

参考代码:

#include
#include
using namespace std;
int a[100001];
/*
    Stuck Point:现在有两个pointers,一个指向前面,一个指向后面,two pointers的思想在于两边夹,但是现在要找
    和为m的两个数,如果两个数和不是m,那么指针是移动那个(移动前面的还是移动后面的)?,就算是把数组按照
    升序的方式排列,也没啥用啊!
    Answer:你没有认真的思考下去,其实就是按照那个方法继续下去,详细请看下面的解题思路
    解题思路:
        1.设置两个pointers,一个指向数组起始位置,一个指向数组最后一个位置
        2.对数组进行排序
        3.如果两个pointer所加的数>目标数字,则将后面的pointer向前移动;否则将前面的向前移动
        4.如果前面的指针所指位置>后面指针所指位置,则表示没有要求的
*/
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=0; i<n; i++)
        cin >> a[i];
    int p=0,q=n-1;   //设置两个下标,一个指向数组的起始位置,一个指向数组的最后一个位置
    sort(a,a+n);
    while(p<q)
    {
        if(a[p]+a[q]==m)
            break;
        if(a[p]+a[q]>m)
            q--;
        else
            p++;
    }
    if(p>=q)
    {
        cout << "No Solution" << endl;
        return 0;
    }
    cout << a[p] << " " << a[q];
    return 0;
}

你可能感兴趣的:(《算法笔记上机实验指南》)