pat A1085
生词:
sequence 一连串
parameter 范围
题意:
输入:
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<iostream>
#include<algorithm>
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<iostream>
#include<algorithm>
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<iostream>
#include<algorithm>
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;
}
pat A1010
单词:
1.decimal 十进位的
2. radix 基数
题意:
输入:
1.输入4个值 N1 N2 tag radix
输出:
1.一个数如果能通过进制的转化成为另一个数,则输出该进制;否则输出impossible
解题思路:
本题的二分思想体现在:现有两个数字,一个是目标数字,一个是变化数字,想办法找出变化数字所能有的进制数的上界和下界,通过二分的方法与目标数字进行比较,如果小表示进制小,则left=mid+1;如果大表示进制大,则right=mid-1
对于同一个字符长度的字符数组,如果进制数越大,则其转化为10进制也越大
解题思路:
1.题目中说N1,N2不超过10位数字,因为后面涉及进制的转化,所以设置为long long类型
2.设置inf表示long long类型取值的上界:是64位的整型,取值范围为-2^63 ~ (2^63 - 1)。
3.6个函数:
1.void init() 将09,az与0~35的对应
2.LL convertNum10(char a[],LL radix, LL t) 将数字字符串a转化为进制为radix的数
3.int cmp(char N2[],LL radix, LL t) 将N2通过convertNum10转化为radix进制放到num中,注意num大,返回1;num小,返回-1;相同,返回0
4.LL binarySearch(char N2[], LL left, LL right, LL t) 利用二分结合函数cmp的返回值确定最终的进制数ans
5.int findLargestDigit(char N2[]) 求出N2中最大数的位数,最终确定进制的下界
4.主函数:
1.先进行数字的映射
2.输入N1,N2,tag,radix,如果tag为2,则交换N1和N2
3.t为N1的radix进制数
4.low为进制的下界
5.high为low,t中较大值+1表示进制的上界
6.调用二分进制数得到ans,根据ans进行输出
参考代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
LL Map[256];
LL Inf=(1LL<<63)-1;
int tag;
LL radix;
char N1[11],N2[11],temp[11]; //注意:N1,N2是字符数组不是LL类型
void init() //将0--9和a--z映射到0--35
{
for(char c='0'; c<='9'; c++)
{
Map[c]=c-'0'; //将'0'~'9'映射到0--9
}
for(char c='a'; c<='z'; c++)
{
Map[c]=c-'a'+10; //将a~z映射到10~35
}
}
LL convertNum10(char a[],LL radix, LL t)
{
//将a转换为10进制,t为上界
LL ans=0;
int len=strlen(a);
for(int i=0; i<len; i++)
{
ans=ans*radix+Map[a[i]]; //进制转化
if(ans<0 || ans>t)
return -1; //溢出或超出N1的10进制
}
return ans;
}
int cmp(char N2[],LL radix, LL t) //N2的10进制与t比较
{
LL num=convertNum10(N2,radix,t); //将N2转化为10进制
if(num<0)
return 1; //溢出,肯定是N2>t,表示进制大了
if(t>num)
return -1; //t较大,返回-1,表示进制小了
else if(t==num)
return 0; //相等,返回0
else //num较大的话返回1
return 1; //num较大,返回1,表示进制大了
}
LL binarySearch(char N2[], LL left, LL right, LL t) //t表示将N1转化为进制为radix的十进制数
{
//二分求解N2的进制
LL mid;
while(left<=right)
{
mid=(left+right)/2;
int flag=cmp(N2,mid,t); //判断N2转化为10进制后与t比较
if(flag==0) //说明t==m
return mid; //找到解,返回mid
else if(flag==-1) //说明t>num
left=mid+1; //往右字区间继续查找
else //说明num较大
right=mid-1; //往左字区间继续查找
}
return -1; //解不存在
}
int findLargestDigit(char N2[])
{
//求最大位数
int ans=-1,len=strlen(N2);
for(int i=0; i<len; i++)
{
if(Map[N2[i]]>ans)
{
ans=Map[N2[i]];
}
}
return ans+1; //最大的位数为ans,说明进制数的底线是ans+1
}
int main()
{
init();
cin >> N1 >> N2 >> tag >> radix;
if(tag==2)
{
strcmp(N1,temp);
strcmp(N2,N1);
strcmp(N2,temp);
}
LL t=convertNum10(N1,radix,Inf);
LL low=findLargestDigit(N2);
LL high=max(low,t)+1;
int ans=binarySearch(N2,low,high,t);
if(ans==-1)
cout << "Impossible" << endl;
else
cout << ans << endl;
return 0;
}
知识总结:
1.strcmp和strcpy的用法:
//strcpy的用法:
#include<iostream>
#include<cstring> //必须加入刺头文件
using namespace std;
int main()
{
char s[1000],s1[1000]="ipad pro";
strcpy(s,s1); //将s1拷贝给s,只能用于数字字符串
cout << s;
return 0;
}
//strcmp的用法:
#include<iostream>
#include<cstring> //必须加入刺头文件
using namespace std;
int main()
{
//两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止
//如果s<s1,则返回-1;如果s=s1,则返回0; 如果s>s1,则返回1
char s[100],s1[100];
cin >> s >> s1;
cout << strcmp(s,s1);
return 0;
}
2.将0-9和a-z分别映射为0–35
void init() //将0--9和a--z映射到0--35
{
for(char c='0'; c<='9'; c++)
{
Map[c]=c-'0'; //将'0'~'9'映射到0--9
}
for(char c='a'; c<='z'; c++)
{
Map[c]=c-'a'+10; //将a~z映射到10~35
}
}
3.将一个字符串数组a,按照进制radix,上界为t转化为long long类型的整数
LL convertNum10(char a[],LL radix, LL t)
{
//将a转换为10进制,t为上界
LL ans=0;
int len=strlen(a);
for(int i=0; i<len; i++)
{
ans=ans*radix+Map[a[i]]; //进制转化
if(ans<0 || ans>t)
return -1; //溢出或超出N1的10进制
}
return ans;
}
4.给出一个字符数组,能求出它的进制的最小数
比如111011010,他的最小进制为2 ; 1223,它的最小进制为3,当然也可以为10
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char N2[100]="1101";
int ans=-1;
int len=strlen(N2);
for(int i=0; i<len; i++)
{
if((N2[i]-'0')>ans)
ans=(N2[i]-'0');
}
cout << ans+1;
return 0;
}
5.计算2^63-1和typedef
typedef long long LL;
LL inf=(1LL << 63)-1; //long long的最大值2^63-1
6.数字,大小写字母的ascii表
pat A1044
题意:
输入:
1.输入n,然后下面输入n个值
2.输入m,表示所要构成的数字之和
输出:
1.输出连续的数字能使其和为m的所有下标,如果没有为m的,就直接输出大于m的最小值的所有下标
解题思路:
解题思路:
1,设置upper_bound函数
1.作用:利用二分找出第一个比x大的数的位置下标
2.初始化数组sum[0]=0
3.从下标为1开始输入数字
4.设置后面的for:
1.先找出nearS:
1.用upper_bound从i~n+1的位置,找出比sum[i]+S大的第一个数的下标
2.如果sum[j-1]-sum[i-1]的值为S,则令nears=s,并退出循环
3.如果值不相同,则就找出比S大的最小值,然后也令nearS为此值即sum[j]-sum[i-1]
5.后面利用upper_bound函数,找出比nearS大的第一个数,但此时计算的时候还是用sum[j-1]-sum[i-1]看是否为nearS,如果等于就
输出
参考代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010;
int sum[N];
int n,S,nearS=100000010;
//upper_bound函数返回(L,R)内第一个大于x的位置
/*
解题思路:
1,设置upper_bound函数
1.作用:利用二分找出第一个比x大的数的位置下标
2.初始化数组sum[0]=0
3.从下标为1开始输入数字
4.设置后面的for:
1.先找出nearS:
1.用upper_bound从i~n+1的位置,找出比sum[i]+S大的第一个数的下标
2.如果sum[j-1]-sum[i-1]的值为S,则令nears=s,并退出循环
3.如果值不相同,则就找出比S大的最小值,然后也令nearS为此值即sum[j]-sum[i-1]
5.后面利用upper_bound函数,找出比nearS大的第一个数,但此时计算的时候还是用sum[j-1]-sum[i-1]看是否为nearS,如果等于就
输出
*/
int upper_bound(int L, int R, int x)
{
int left=L,right=R,mid;
while(left<right)
{
mid=(left+right)/2;
if(sum[mid]>x)
{
right=mid;
}
else
{
left=mid+1;
}
}
return left;
}
int main()
{
cin >> n >> S; //元素个数,和值S
sum[0]=0; //初始化sum[0]=0
for(int i=1; i<=n; i++)
{
cin >> sum[i];
sum[i]+=sum[i-1]; //求sum[i]
}
for(int i=1; i<=n; i++)
cout << sum[i] << " ";
cout << endl;
//sum[j-1]-sum[i-1],这里所求和为S,则i-1~j-1就为和S
//sum[j]-sum[i-1],表示从i到j的数字之和
//下面第一个for语句的作用是如果能找出和为S的数组,就让nears和S,否则就找出比S大的第一个数
for(int i=1; i<=n; i++) //枚举左端点,先找出nearS,如果是满足的nearS则直接让nearS=S,否则就是找出比S大的最小nears
{
int j=upper_bound(i,n+1,sum[i-1]+S); //找出第一个比参考数大的第一个数的位置
if(sum[j-1]-sum[i-1]==S) //查找成功(注意是j-1而不是j)
{
nearS=S; //最接近s的值就是S
break;
}
else if(j<=n && sum[j]-sum[i-1]<nearS) //统计出比S大的最小数值
{
//存在大于s的解并小于nearS
nearS=sum[j]-sum[i-1]; //更新当前nearS
}
//直接找值满足nearS数组的下标
for(int i=1; i<=n; i++)
{
int j=upper_bound(i,n+1,sum[i-1]+nearS); //求右端点
if(sum[j-1]-sum[i-1]==nearS) //查找成功
cout << i << "-" << j-1 << endl; //输出左端点和右端点(注意是j-1而不是j)
}
return 0;
}
}
注意事项:
1.注意n的取值范围,所以才设置数组长度时要大于100000
2.注意在写二分的函数时,每次要改变mid的值,所以要在循环内设置mid=(left+right)/2
pat A1048
题意:
输入数字n,m;后面连续输出n个数字
在n个数字中找出v1+v2==m,并且v1<=v2的数,如果没有则直接输出no solution
输入:
1.输入n,然后下面输入n个值
2.输入m,表示所要构成的数字之和
输出:
如果v1+v2==n,并且v1<=v2的
解题思路:
解题思路:
0.这道题用常规的二分思想,就是while(i<=j) 然后 a[mid]>x,则right=left-1,如果a[mid]<x,则left=mid+1,最后,如果值相等,则返回他的下标
1.这道题的二分思想是,先输入数组,然后将数字进行排序,然后遍历每一个数字,用二分的思想从这个数后面的
一个位置一直到结束,开始查找它的差(m-v[i])。如果找到则放回下标,否则返回-1,一旦找到,就是最小的,直接输出然后就退出循环
2.最后根据i的位置是否为n,来是否输出no solution
参考代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100010];
/*
*/
int Bin(int left, int right, int key)
{
int mid;
while(left<=right)
{
mid=(left+right)/2;
if(a[mid]==key)
return mid;
else if(a[mid]>key)
right=mid-1;
else
left=mid+1;
}
return -1; //表示咩有找到
}
int main()
{
int i,n,m;
cin >> n >> m;
for(int i=0; i<n; i++)
cin >> a[i];
sort(a,a+n);
for(i=0; i<n; i++)
{
int pos=Bin(i+1,n-1,m-a[i]);
if(pos!=-1) //如果找到合适的并且i!=pos,其中i!=pos表示2*v1==m,然后题目要求是v1<=v2
{
cout << a[i] << " " << a[pos] << endl;
break;
}
}
if(i==n)
cout << "No Solution";
return 0;
}