PAT (Basic Level) Practice (中文)
目录
1001 害死人不偿命的(3n+1)猜想
卡拉兹(Callatz)猜想:
对任何一个正整数 n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把 (3n+1) 砍掉一半。这样一直反复砍下去,最后一定在某一步得到 n=1。卡拉兹在 1950 年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证 (3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……
我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过 1000 的正整数 n,简单地数一下,需要多少步(砍几下)才能得到 n=1?
#include<iostream>
int main()
{
int n = 0, count = 0;
std::cout<<"Please enter a number within 1000:" ;
std::cin>> n ;
while(n!=1){
if(n%2==0){ //如果n是偶数
n = n/2; //砍掉一半
count++; //计数加1
}
else{ //n是奇数
n= (3*n+1)/2; //把(3n+1)砍掉一半
count++; //计数器加1
};
};
std::cout<< n<<"需要" << count <<"步" <<std::endl; //输出从 n 计算到 1 需要的步数。
return 0;
}
1002
1003
1004 成绩排名
读入 n(>0)名学生的姓名、学号、成绩,分别输出成绩最高和成绩最低学生的姓名和学号。
#include<iostream>
#include<vector>
#include <iterator>
using namespace std;
struct stu{ //定义stu类,包含姓名,学号,分数信息
string sname;
string sID;
unsigned int scores = 0;
};
int main()
{
unsigned int n = 0; //输入的人数n
stu stu1;
vector<stu> stvec; //容器stvec用来存放输入的学生的信息
if(cin>>n){ //确保有数据输入
for(unsigned int i = 0; i<n; ++i){
cin>>stu1.sname>>stu1.sID>>stu1.scores; //读入学生数据
stvec.push_back(stu1); //将读到的数据存入容器svec中
}
}
for(int i = 0; i < n ; ++i){ //按分数将学生冒泡排序
for(int j = 0; j < n - 1- i; ++j){
if(stvec[j].scores > stvec[j+1].scores){
stu temp = stvec[j]; //交换整个stu,全部学生信息
stvec[j] = stvec[j+1];
stvec[j+1] = temp;
}
}
}
cout<<stvec[stvec.size()-1].sname<<" "<<stvec[stvec.size()-1].sID<<" "<<endl;
cout<<stvec[0].sname<<" "<<stvec[0].sID<<" "<<endl; //输出
system("pause");
return 0;
}
1005 继续(3n+1)猜想
拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。
当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 n=3 进行验证的时候,我们需要计算 3、5、8、4、2、1,则当我们对 n=5、8、4、2 进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这 4 个数已经在验证3的时候遇到过了,我们称 5、8、4、2 是被 3“覆盖”的数。我们称一个数列中的某个数 n 为“关键数”,如果 n 不能被数列中的其他数字所覆盖。
现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。
#include<iostream>
#include<vector>
#include <iterator>
using namespace std;
void scin(int a, vector<int> &iv){ //完成题目要求的输入方式的函数
cin>>a;
int b;
for(int i = 0 ; i != a; ++i){
if(cin>>b)
iv.push_back(b);
}
}
bool invec(const vector<int> &iv ,const int i, vector<int>::size_type &k){ //i是否在容器iv中,并隐式返回所在位置k
for(decltype(iv.size() ) j = 0; j != iv.size(); ++j){
if(iv[j] == i){
k=j;
return 1;
}
}
return 0;
}
void callatz(vector<int> &iv, int n){ //找出一个数的卡拉兹‘衍生数’;
while(n!=0 && n != 1){
if(n%2 == 1){ //i是奇数
n = (3*n + 1)/2; //把(3n+1)砍一半存入iv
iv.push_back(n);
} else { //i是偶数
n = n/2; //把n砍一半存入iv
iv.push_back(n);
}
}
}
void sort(vector<int> &iv){ //冒泡排序
for(decltype(iv.size() ) j = 0; j != iv.size(); ++j){
for(decltype(iv.size() ) i = 0; i != iv.size()-1; ++i){
if(iv[i]<iv[i+1]){
int temp = iv[i];
iv[i]=iv[i+1];
iv[i+1] = temp;
}
}
}
}
int main()
{
int sl = 0;
vector<int> iv1,iv2,iv3; //iv1,输入数据;iv2,卡拉兹衍生数据;iv3目标数据。
scin(sl,iv1);
vector<int>::size_type k;
for(int i=0; i<iv1.size(); ++i){
callatz(iv2,iv1[i]); //找出第一个输入值的卡拉兹衍生数;
for(decltype(iv2.size() ) j = 0; j != iv2.size(); ++j){ //遍历衍生数的容器
if(invec (iv1,iv2[j],k)){ //衍生数是否已在输入的容器中
iv1[k] = 0; //若在,将其置0;
}
}
iv2 = {}; //重置iv2的值,避免重复循环
}
for(auto c : iv1){
if(c != 0)
iv3.push_back(c); //把iv2中所有的非零值,即关键数字存入iv3
}
sort(iv3); //对iv3中元素进行排序
for(auto c : iv3)
cout<<c<<" "; //范围for循环输出
cout<<'\b'<<"end"<<endl; //输出退格符,删除最后一个空格。 但这样写pat上测试不能通过,不知道为什么。。
system("pause");
return 0;
}
1006 换个格式输出整数
让我们用字母 B 来表示“百”、字母 S 表示“十”,用 12...n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过 3 位的正整数。例如 234 应该被输出为 BBSSS1234,因为它有 2 个“百”、3 个“十”、以及个位的 4。
#include<iostream>
#include<vector>
#include <iterator>
using namespace std;
void gsb(const int n, int &a,int &b,int &c){
a = n % 10; //个位
b = (n/10)%10; //十位
c = n/100; //百位
}
int main()
{
int n = 0;
int a,b,c;
if(cin>>n && n<=999)
gsb(n,a,b,c);
for(int i = 0; i !=c; ++i)
cout<<"B";
for(int i = 0; i !=b; ++i)
cout<<"S";
for(int i = 0; i !=a; ++i)
cout<<(i+1);
system("pause");
return 0;
}
1007 素数对猜想
让我们定义dn为dn=pn+1−pn,其中pi是第i个素数。显然有d1=1,且对于n>1有dn是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。
现给定任意正整数N(<10^5),请计算不超过N的满足猜想的素数对的个数
#include<iostream>
#include<vector>
#include <iterator>
using namespace std;
bool isprime(const int &m){ //求素数的算法需提高效率,最后一个测试会超时
if(m == 2)
return true;
if(m % 2 == 0)
return false;
for(int i = 3; i < m-1; i += 2){
if(m%i == 0)
return false;
}
return true;
}
void element(vector<int> &pv, const int &m){ //把小于m的素数存入容器pv
for(int i = 2; i <=m; ++i){
if(isprime(i))
pv.push_back(i);
}
}
int main()
{
int cnt=0;
vector<int> ev{};
int n = 0;
if(cin>>n)
element(ev,n);
for(auto it = ev.begin(); it != ev.end()-1; ++it){
if(*(it+1) - *it == 2)
++cnt;
}
cout<<cnt<<endl;
return 0;
}
1008 数组元素循环右移问题
一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A~0~A~1~…A~N-1~)变换为(A~N−M~⋯A~N−1~A~0~A~1~ ⋯A~N−M−1~)(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
思路:可以把数组看作由下标N-M-1分割的两部分组成,N-M-1之后的AN-M到AN-1和N-M-1之前的A0到AN-M-1
(AN−M⋯AN−1 A0A1 ⋯AN−M−1)
#include<iostream>
#include<vector>
#include <iterator>
using namespace std;
int main()
{
size_t arrs = 0; //定义数组大小
cin>>arrs; //输入数组大小
int arr[arrs]; //定义数组,书上说数组的维度必须是常量表达式(constexpr),但。。
size_t m = 0;
cin>>m; //输入m的值
if(m>arrs) //循环右移,要考虑到位移值m大于数组长度的情况
m = m % arrs;
int a;
int i = 0;
while(cin>>a){
arr[i] = a; //读入数组的值
++i;
}
vector<int> pm {}; //定义容器负责存储数组的前arr-m个数
vector<int> bm {}; //容器bm负责存储数组剩下的 m 个数
for(size_t i = 0; i < arrs-m ; ++i){ //把数组的前arr - m个数存入pm
pm.push_back(arr[i]);
}
//cout<<"pm: ";
//for(auto c:pm)
//cout<<c<<" ";
//cout<<endl;
for(size_t i = arrs-m; i<arrs ; ++i){ //把数组剩下的 m 个数存入bm
bm.push_back(arr[i]);
}
//cout<<"bm: ";
//for(auto c:bm)
//cout<<c<<" ";
//cout<<endl;
for(size_t i = 0; i<m; ++i){
arr[i] = bm[i];
}
for(size_t i = m,j=0; i<arrs&&j<pm.size(); ++i,++j){
arr[i] = pm[j];
}
for(size_t i = 0; i<arrs; ++i){
cout<<arr[i]<<" ";
}
cout<<endl;
system("pause");
return 0;
}
1009 说反话
给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main(){
vector<string> sv;
string s;
while(cin>>s){
sv.push_back(s);
}
for(int i=sv.size()-1;i>=0;--i){ //逆序遍历输出即可
cout<<sv[i];
if(i!=0)
cout<<" ";
}
cout<<endl;
return 0;
}
1010 一元多项式求导
设计函数求一元多项式的导数。
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> iv;
vector<int> cv;
int a;
while(cin>>a){
iv.push_back(a);
}
for(decltype(iv.size()) i=0;i<iv.size()-1;i+=2){
if(iv[i+1]!=0){
cv.push_back(iv[i]*iv[i+1]);
cv.push_back(iv[i+1]-1);
} else if(iv.size() == 2) { //注意考虑题目中提到的一种特殊情况,即求导后位零多项式的情况
cv.push_back(0);
cv.push_back(0);
}
}
for(decltype(cv.size()) i=0;i<cv.size();++i){
cout<<cv[i];
if(i!=cv.size()-1)
cout<<" ";
}
cout<<endl;
return 0;
}
1011 A+B 和 C
给定区间 [−231 ,231] 内的 3 个整数 A、B 和 C,请判断 A+B 是否大于 C
#include<iostream>
#include<vector>
using namespace std;
int main(){
int count;
cin>>count;
vector<long> iv; //注意用long,int只有16位,会溢出
long a;
for(int i=0;i<count;++i){
for(int j=0;j<3;++j){
cin>>a;
iv.push_back(a);
}
if(iv[0]+iv[1]>iv[2]){
cout<<"Case #"<<i+1<<": true"<<endl;
} else {
cout<<"Case #"<<i+1<<": false"<<endl;
}
iv={};
}
return 0;
}