《算法笔记》第4章 入门篇(2)---算法初步 4.7其他高效技巧和算法

4.7.1 打表

在这里插入图片描述

4.7.2 活用递推:

pat A1093

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

输入:

1.输入一个字符串

输出:

输出pat的个数
在这里插入图片描述

解题思路1:

在这里插入图片描述

参考代码1:
#include<iostream>
#include<cstring>
using namespace std;
/*
    解题思路:
    1.只要找到一个A,用它左边的p的个数*它右边p的个数,将所有的乘积结果累加,就是最终结果
    2.统计每一个位置它左边A的个数,包含当前位,用if-else语句:如果字符为'p'则加1,如果不是则就等于上一位
    3.从后到前,如果遇到T,则将T的个数累加,然后求出的结果和此时A所对应的位置的下标表示的p的个数相乘,然后将结果累加起来,不要忘记对mod取模
*/
const int MAXN=100010;
const int MOD=1000000007;
char str[MAXN];  //字符串
int leftNump[MAXN]={0};   //每一个左边含p个数
int main()
{
    cin >> str;   //读入字符串
    int len=strlen(str);     //长度
    for(int i=0; i<len; i++)
    {
        //从左到右遍历字符串
        if(i>0)
        {
            //如果不是0号位
            leftNump[i]=leftNump[i-1];  //继承上一位的结果
        }
        if(str[i]=='P')   //当前位是p
        {
            leftNump[i]++;  //令leftNump[i]加1
        }
    }
    int ans=0,rightNumT=0;   //ans为答案,rightNumT记录右边T的个数
    for(int i=len-1; i>=0; i--)
    {
        //从右到左遍历字符串
        if(str[i]=='T')   //当前位是T
        {
            rightNumT++;    //右边T个数加1
        }
        else if(str[i]=='A')   //当前位是A
        {
            ans=(ans+leftNump[i]*rightNumT)%MOD;  //累计乘积,注意要对结果进行求余
        }
    }
    cout << ans;       //输出结果
    return 0;
}

解题思路2:

在这里插入图片描述

参考代码2:
#include <iostream>
#include <string>
using namespace std;
//统计出来每个A左边P的个数和右边T的个数然后相乘累加即可
int main()
{
    string s;
    cin >> s;
    int p=0,t=0,ans=0;
    for(int i=0; i<s.length(); i++)
        if(s[i]=='T')
            t++;
    for(int i=0; i<s.length(); i++)
    {
        if(s[i]=='P')
            p++;
        if(s[i]=='T')
            t--;
        if(s[i]=='A')
            ans=(ans+(p*t)%1000000007)%1000000007;
    }
    cout << ans;
    return 0;
}

4.7.3 随机选择算法:

1.求出第k大的数

在这里插入图片描述

int randselection(int A[], int left, int right, int k)
{
    if(left==right)   //如果left==right表示处在边界,直接返回A[left]
        return A[left];
    int p=randpartation(A,left,right);   //用随机的快速排序的方法p的位置求出,p所在的位置表示,左边的数都比他小,右边的数都比他大
    int m=p-left+1;   //实际算出来的值,用p计算出来m的值
    if(m==k)       //如果实际求出来的m等于k,则直接返回A[p]
        return A[p];
    else if(m>k)      //如果实际求出来的值比k大,表示应该在左侧进行寻找
        return randselection(A,left,p-1,k);
    else if(m<k)
        return randselection(A,p+1,right,k-m);  //反之,应该在右侧寻找
}

2.实际的一个应用:

在这里插入图片描述

常规解决思路:
在这里插入图片描述

优化解决思路:
在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int randpartition(int A[],int left,int right)   //进行随机快速排序的函数,作用:找出能使左边的数都比他小,右边的数都比他的大数的位置
{
    int p=(round(1.0*rand()/RAND_MAX*(right-left))+left);  //随机找出p的位置
    swap(A[left],A[right]);     //交换A[left]与A[right]的值
    int temp=A[left];
    while(left<right)
    {
        while(left < right && A[right]>temp)
        {
            right--;
        }
        A[left]=A[right];
        while(left < right && A[left]<temp)
        {
            left++;
        }
        A[right]=A[left];
    }
    A[left]=temp;
    return left;
}

int randselect(int A[],int left,int right,int k)   //结合randpartition函数,找出第k大的数
{
    if(left==right)         //记住这里表示在边界
        return A[left];
    int p=randpartition(A,left,right);     //设置p为上面函数所求的位置
    int m=p-left+1;      //m表示实际算出的结果
    if(m==k)         //如果直接相同,返回该数字
        return A[p];
    else if(m>k)     //若大于,则在左边进行递归
        return randselect(A,left,p-1,k);
    else if(m<k)      //若小于,则在右边进行递归
        return randselect(A,p+1,right,k-m);
}
int main()
{
    int A[110001];
    srand((unsigned)time(NULL));
    int sum=0,sum1=0,temp1;
    int n;
    cin >> n;
    for(int i=0; i<n; i++)
    {
        cin >> A[i];        //输入所求数字
        sum+=A[i];                   //并求出总和来
    }
    randselect(A,0,n-1,n/2);       //找出前n/2大的数字
    for(int i=0; i<n/2; i++)      //统计出来较小的一组数字
        sum1+=A[i];
    cout << (sum-sum1)-sum1 << endl;      //求出两个部分数组的数字之和的差
    //注意当代码完成后,A不一定是按照升序排列,两部分的数组中的数字的顺序不一定按照升序排列,可以是乱序,但是保证两个部分数组是绝对分开的
    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值