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;
}