PAT乙级1001-1020
乙级题目很多基础题,加上一些针对C语言的特定坑。
用python直接砍瓜切菜。
每20道题开个新帖
1001 害死人不偿命的(3n+1)猜想
#include<stdio.h>
int main(){
int d;
scanf("%d",&d);
int times = 0;
while(d != 1){
if (d%2){ //奇数
d = (3 * d + 1 )/ 2;
}else{ //偶数
d = d / 2;
}
++times;
}
printf("%d",times);
return 0;
}
1002 写出这个数
#include<iostream>
#include<string>
#include<vector>
using namespace std;
vector<string> chinese = {
"ling",
"yi",
"er",
"san",
"si",
"wu",
"liu",
"qi",
"ba",
"jiu"
};
int main(){
string inp;
cin >> inp;
int rint = 0;
for (auto c:inp){
rint += c - 48;
}
string rstr = to_string(rint);
string outs;
for (int i = 0 ; i != rstr.size() - 1 ; ++i){
outs += chinese[rstr[i]-48] + " ";
}
outs += chinese[rstr[rstr.size() - 1] - 48];
cout << outs;
return 0;
}
1003 我要通过
#include<iostream>
#include<string>
#include<vector>
using namespace std;
bool test_ok(string &);
int main(){
int num;
cin >> num;
vector<bool> result(num,false);
string input;
for(int i = 0 ; i != num ; ++i){
cin >> input;
result[i] = test_ok(input);
}
for(int i = 0 ; i <= num - 1 ; ++i){
if (result[i])
cout << "YES" <<endl;
else cout << "NO" << endl;
}
return 0;
}
bool test_ok(string &ss){
bool P = false,T = false; //分别表示P或者T有没有出现
int foreA = 0, middA = 0, tailA= 0;//分别表示P前,PT中间,T后的A的数量
bool ok = true;
for (auto c:ss){
if (c == 'A'){
if (!P)
++foreA;
else if (!T)
++middA;
else ++tailA;
}else if (c == 'P'){
if (P || T){ //重复出现了P,或者在T后面出现了P
ok = false;
break;
}else
P = true;
}else if (c == 'T'){
if (!P || T){//出现在P前面或者已经出现过T
ok = false;
break;
}else
T = true;
}else{ //非PAT字符
ok = false;
break;
}
}
if (!ok)
return false;
if (!P || !T)
return false;
if (middA == 0)
return false;
if (foreA == tailA && middA == 1)
return true;
else if (foreA == 0 && tailA == 0)
return true;
else if (tailA == middA * foreA)
return true;
else return false;
}
1004 成绩排名
d = int(input())
min = 101
max = -1
for i in range(0,d):
line = input().split(' ')
line[2] = int(line[2])
if line[2] > max:
max = line[2]
high= line
if line[2] < min:
min = line[2]
low = line
print(high[0],high[1])
print(low[0],low[1])
1005 继续3N+1猜想
K = int(input())
seq = input().split(' ')
seqn = [int(c) for c in seq]
done = [] #已验证过的
for c in seqn:
while c != 1:
if c % 2:
c = (3 * c + 1 )/2
done.append(c)
else:
c = c / 2;
done.append(c)
for i in done:
if i in seqn:
seqn.remove(i)
seqn.sort(reverse = True)
for i in seqn[:-1]:
print(i,end=" ")
print(seqn[-1])
1006 换个格式输出整数
c = int(input())
outstr=""
if int(c/100):
outstr = outstr + "B"*int(c/100);
c = c % 100
if int(c/10):
outstr = outstr + "S"*int(c/10);
c = c % 10
for i in range(1,c+1):
outstr = outstr + str(i)
print(outstr)
1007 素数对猜想
//难点在于素数测试函数的设计,只要够高效就能过
//思路是在传参到素数测试函数时就只测奇数,在素数测试函数内部,也只用奇数当因子
//而且最大测到平方根,因为超过平方根以后显然就成了3*5和5*3的关系,不用测。
#include<stdio.h>
#include<math.h>
int is_su(int);
int main(){
int top;
scanf("%d",&top);
int result = 0;
for ( int i = 3 ; i <= top - 2 ; i += 2){ //保证传过去的都是奇数
if (is_su(i) && is_su(i+2))
++result;
}
printf("%d",result);
return 0;
}
int is_su(int test){
int bbb = 1;
for (int i = 3 ; i <= (int)sqrt(test) ; i += 2){ //偶数因子不用测
if ( !(test % i) )
bbb = 0;
}
return bbb;
}
1008 数组元素循环右移
#include<stdio.h>
void print_nums(int *, int);
int gcd(int a ,int b);
int main(){
int len,M;
scanf("%d %d",&len,&M);
M = M % len;
int nums[len];
for(int i = 0 ; i != len ; ++i){
scanf("%d",&nums[i]);
}
if ( !M ) //M是0,直接输出
print_nums(nums,len);
else{
int yueshu = gcd(len,M);
for (int i = 0 ; i != yueshu ; ++i){
int reserve = nums[ i - M + len];
for(int j = 0 ; j != len / yueshu ; ++j ){
int temp = nums[(i + j * M) % len];
nums[(i + j * M) % len] = reserve;
reserve = temp;
}
}
print_nums(nums,len);
}
return 0;
}
void print_nums(int *nums,int len){
for (int i = 0 ; i != len -1 ; ++i){
printf("%d ",nums[i]);
}
printf("%d",nums[len-1]);
}
int gcd(int a,int b){
return b==0 ? a : gcd(b,a%b);
}
1009 说反话
#python简直是送的,翻转链表?不存在的,不知道什么叫翻转链表。
input_words = input().split(' ')
input_words.reverse()
output_str = " ".join(input_words).strip();
print(output_str)
1010 一元多项式求导
#include <stdio.h>
int main()
{
int m,n;
int flag=1;
while(scanf("%d %d",&m,&n)!=EOF){
if(n>0){
if(flag==1){
printf("%d %d",m*n,n-1);
flag=0;
}else{
printf(" %d %d",m*n,n-1);
}
}
}
if(flag==1)
printf("0 0");
return 0;
}
1011 A+B和C
#考察C语言的溢出,不过是正数A + B < A、B了,就说明上溢出
#负数A + B > A、B,就说明下溢出,然而我才不用C语言写呢
#上python,去你娘的C语言溢出嘞
n = int(input())
for i in range(0,n):
nums = [int(i) for i in input().split(' ')]
A = nums[0]
B = nums[1]
C = nums[2]
print("Case #{}: {}".format(i+1,"true" if A + B > C else "false"))
1012 数字分类
#有时喜欢def main,有时就直接上了
from decimal import Decimal
input_line = [Decimal(x) for x in input().split(' ')]
num = input_line[0]
input_line = input_line[1:]
exists = [False] * 5
A = [0] * 5
A2_fh = 1
A4_num = 0
for x in input_line:
if x % 5 == 0:
if x % 2 == 0:
A[0] += x
exists[0] = True
elif x % 5 == 1:
exists[1] = True
A[1] += A2_fh * x
A2_fh *= -1
elif x % 5 == 2:
exists[2] = True
A[2] += 1
elif x % 5 == 3:
exists[3] = True
A[3] += x
A4_num += 1
elif x % 5 == 4:
exists[4] = True
if x > A[4]:
A[4] = x
if exists[3]:
A[3] = round(A[3]/A4_num,1)
if exists[0]:
print(A[0],end="")
else: print("N",end = "")
for i in range(1,5):
if exists[i]:
print(" {}".format(A[i]),end = "")
else:
print(" N",end = "")
1013 数素数
//按2步进,应该是最优方式了
#include<stdio.h>
#include<math.h>
int is_su(int);
int is_su(int test){
int bbb = 1;
for (int i = 3 ; i <= (int)sqrt(test) ; i += 2){ //偶数因子不用测
if ( !(test % i) )
bbb = 0;
}
return bbb;
}
int main(){
int begin,end;
scanf("%d %d",&begin,&end);
int pos = 1;
int line = 0;
if (begin == 1){
if (end != 1)
printf("2 ");
else printf("2");
line = 1;
}
for (int i = 3 ; ; i += 2){
if (is_su(i)){
pos += 1;
if (pos >= begin && pos <= end){
if (line < 9 && pos != end)
printf("%d ", i);
else
printf("%d\n",i);
line += 1;
if (line >= 10) line -= 10;
}
if (pos > end) break;
}
}
return 0;
}
1014 福尔摩斯的约会
#有些莫名其妙的坑,简直都懒得去排除,什么玩意
n1=input()
n2=input()
a=[]
b=[chr(x) for x in range(ord("A"),ord("Z")+1)]
c=["MON","TUE","WED","THU","FRI","SAT","SUN"]
for i in range(min(len(n1),len(n2))):
if "A"<=n1[i]<="G" and n1[i]==n2[i]:
a.append(n1[i])
k=i
break
for i in range(k+1,min(len(n1),len(n2))):
if "A"<=n1[i]<="N" and n1[i]==n2[i]:
a.append(str(b.index(n1[i])+10))
break
elif "0"<=n1[i]<="9" and n1[i]==n2[i]:
a.append("0"+n1[i])
break
m1=input()
m2=input()
for i in range(min(len(m1),len(m2))):
if ("A"<=m1[i]<="Z" or "a"<=m1[i]<="z") and m1[i]==m2[i]:
if i<10:
i="0"+str(i)
a.append(str(i))
break
print("%s %s:%s"%(c[b.index(a[0])],a[1],a[2]))
1015 德才论
正确但效率不高的解法
//超时了三个节点,看来还是要用树来保存
//我特么以为超时是需要用树来保存,发现并不是,发现是cin和cout效率太低了
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
struct node{
string ID;
int de;
int cai;
node(const string &s,const int &a ,const int &b):ID(s),de(a),cai(b){};
};
bool be_greater(const node &a ,const node &b){ //a比b大时返回真
if (a.de + a.cai > b.de + b.cai)
return true;
else if (a.de + a.cai < b.de + b.cai)
return false;
else if (a.de + a.cai == b.de + b.cai){
if (a.de > b.de)
return true;
else if (a.de < b.de)
return false;
else if (a.ID < b.ID)
return true;
else return false;
}
}
void print_res(vector<node> &res){
for(auto e:res){
cout << e.ID << " " << e.de << " " << e.cai << endl;
}
}
int main(){
int N,L,H;
cin >> N >> L >> H;
vector<vector<node>> degree(4,vector<node>());
int all = 0;
for (int i = 0 ; i != N ; ++i){
string ID;
int de,cai;
cin >> ID >> de >> cai;
if (de < L or cai < L){
continue;
}else if (de >= H and cai >= H){ //第一类人
degree[0].push_back(node(ID,de,cai));
++all;
}else if (de >= H and cai < H){
degree[1].push_back(node(ID,de,cai));
++all;
}else if (de < H and cai < H and de >= cai){
degree[2].push_back(node(ID,de,cai));
++all;
}else{
degree[3].push_back(node(ID,de,cai));
++all;
}
}
cout << all << endl;
for(int i = 0 ; i != 4 ; ++i){
sort(degree[i].begin(),degree[i].end(),be_greater);
print_res(degree[i]);
}
return 0;
}
另一个解法
//发现是cin和cout的锅已经来不及了,我都给换成堆了……
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
struct node
{
int ID;
int de;
int cai;
node(const int &s, const int &a, const int &b) : ID(s), de(a), cai(b){};
};
struct be_greater
{
bool operator()( const node &b ,const node &a )
{
if (a.de + a.cai > b.de + b.cai)
return true;
else if (a.de + a.cai < b.de + b.cai)
return false;
else if (a.de + a.cai == b.de + b.cai)
{
if (a.de > b.de)
return true;
else if (a.de < b.de)
return false;
else if (a.ID < b.ID)
return true;
else
return false;
}
};
};
using min_heap = priority_queue<node, vector<node>, be_greater>;
void print_res(min_heap &res)
{
while(!res.empty()){
auto a = res.top();
printf("%d %d %d\n",a.ID,a.de,a.cai);
res.pop();
}
}
int main()
{
int N, L, H;
scanf("%d %d %d",&N,&L,&H);
vector<min_heap> degree(4);
int all = 0;
for (int i = 0; i != N; ++i)
{
int ID , de, cai;
scanf("%d %d %d",&ID,&de,&cai);
if (de < L or cai < L)
{
continue;
}
else if (de >= H and cai >= H)
{ //第一类人
degree[0].push(node(ID, de, cai));
++all;
}
else if (de >= H and cai < H)
{
degree[1].push(node(ID, de, cai));
++all;
}
else if (de < H and cai < H and de >= cai)
{
degree[2].push(node(ID, de, cai));
++all;
}
else
{
degree[3].push(node(ID, de, cai));
++all;
}
}
printf("%d\n",all);
for (int i = 0; i != 4; ++i)
{
print_res(degree[i]);
}
return 0;
}
针对iostream超时的一些思考
cin慢是有原因的,其实默认的时候,cin与stdin总是保持同步的,也就是说这两种方法可以混用,而不必担心文件指针混乱,同时cout和stdout也一样,两者混用不会输出顺序错乱。正因为这个兼容性的特性,导致cin有许多额外的开销,实际效率差距高达四到五倍。
只需一个语句std::ios::sync_with_stdio(false)禁止这个特性,就可以取消cin于stdin的同步,此时cin的效率和scanf就差不多了。
ios::sync_with_stdio(false);
对一切提供文件的函数来说,其实还有一种办法,是用fread()将整个文件一口气全部读入,效率测试比scanf()还要快10倍。
针对c++ callable的一些思考
以下内容截取自《C++Primiere 中文第五版》511页。
C++中有几种可调用的对象:函数、函数指针、lambda表达式,bind创建的对象以及重载了函数调用运算符的类。比如说:
int add(int a,int b){return a + b;};
auto mod = [](int a ,int b) -> int {return a % b;}; //-> int 是lambda的尾置返回值,此例中可省略
struct divide{
int operator () (int a,int b){
return a / b;
}
};
//bind太复杂了这里先不探讨
和其他对象一样,可调用的对象也有类型,比如每个lambda有特自己唯一的(未命名的)类类型,函数及函数指针的类型则由其返回值类型和实参类型决定。需要注意的是,函数和函数指针,其实与其他可调用类型还有所不同,它并不是一个类,因此不能使用在一些要求可调用类的模板中,比如上面的这个例子,priority_queue的定义为:
template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
第三个参数要求是一个可调用的类型,但传入函数和函数指针就不行,会出错,因此只能像上面那样,创建了一个可调用的类类型:
struct be_greater
{
bool operator()( const node &b ,const node &a )
{
.....
}
};
然而,两种不同类型的可调用对象却可能共享一种调用形式。调用形式指明了调用返回的类型以及传递给调用的实参类型,一种调用形式对应一个函数类型,例如:
int (int int)
是一个函数类型,接受两个int为参数,返回一个int,比如上面例子中的可调用对象,全部都是这种可调用类型的。
我们有时候可能需要一种更普遍的方式来管理这些可调用对象,比如我们想要把他们放入一个vector里轮番调用的时候,因为他们的各自的类型不同,所以无法放入vector。对于这个问题,我们可以使用C++11引入的function类型来解决这个问题,将所有的类型统一为一个function类型。
function类型这样定义:
#include<functional>
function<int(int,int)> f1 = add;
function<int(int,int)> f2 = mod;
function<int(int,int)> f3 = divide();
//这里的()不是调用的意思,而是构造一个divide类对象,因为divide是一个struct
可惜的是,因为function必须实例化之后才能对应一个可调用对象,因此他也不能传给priority_queue使用,如下的调用是错误的。
#include<functional>
//先定义一个函数
bool be_greater( const node &b ,const node &a ){
if (a.de + a.cai > b.de + b.cai)
return true;
else if (a.de + a.cai < b.de + b.cai)
return false;
else if (a.de + a.cai == b.de + b.cai)
{
if (a.de > b.de)
return true;
else if (a.de < b.de)
return false;
else if (a.ID < b.ID)
return true;
else
return false;
}
}
//用它初始化一个function
function<bool(const node &a,const node &b)> f1 = be_greater;
//把这个function传给priority_queue
using min_heap = priority_queue<node, vector<node>, f1>; //注意,是错误的。
1016 部分A+B
inputline = input().split(' ')
PA = inputline[1] * inputline[0].count(inputline[1])
PB = inputline[3] * inputline[2].count(inputline[3])
print(int(PA or "0") + int(PB or "0"))
#这里的一个小坑,在于如果count的结果是0,那么PA就是空字符串,int("")会出错。所以要or一下
#input一行,其实还可以更简便地写成下面这种形式,这也是python的一个小技巧
a,aa,b,bb = input().split(' ')
1017 A除以B
python的偷懒解法
#用python可以用内置函数超简单地做,如下,但是这道题的边界处理还有点意思,可以考虑用C++划一波
a , b = input().split(' ')
a = int(a)
b = int(b)
q,r = divmod(a,b)
peint(q,r)
C++的正常解法
//C++的解法就要考虑怎么解,很简单的想法就是按正常的除法来借位
//这种基础题最好不要想用什么奇技淫巧,因为往往本来很简单的题,奇技淫巧载在莫名其妙的边界问题上
#include<iostream>
#include<string>
using namespace std;
int main(){
string a;
int b;
cin >> a >> b;
int lastr = 0;
//处理一种边界情况,就是第一位就不够除,这时候不能输出0的
if ( (a[0] - '0') < b){
lastr = a[0] - '0';
if (a.size() == 1){
cout << 0 << " " << a;
return 0;
}
}else{
cout << (a[0] - '0') / b;
lastr = (a[0] - '0') % b;
}
for ( int i = 1 ; i != a.size() ; ++i){
cout << (lastr * 10 + (a[i] - '0')) / b;
lastr = (lastr * 10 + (a[i] - '0')) % b;
}
cout << " " << lastr;
return 0;
}
1018 锤子剪刀布
解法
#include<stdio.h>
#define CHUI 0
#define JIAN 1
#define BU 2
#define WIN 0
#define TIE 1
#define LOSE 2
int iswinner(char A,char B){
if (A == 'C' && B == 'J'){
return 1;
}else if (A == 'J' && B == 'B'){
return 1;
}else if (A == 'B' && B == 'C'){
return 1;
}
return 0;
}
int get_index(char A){
if (A == 'C')
return CHUI;
else if (A == 'J')
return JIAN;
else return BU;
}
int max(int a,int b){
return a > b ? a : b;
}
char get_most_win(int a, int *b){
if (b[BU] == a)
return 'B';
else if (b[CHUI] == a)
return 'C';
else return 'J';
}
int main(){
int N;
scanf("%d",&N);
int jiawin[3],yiwin[3];//分别表示甲乙赢的时候CJB的次数,其实可以省略一个,因为倒序输出一次就OK了
int jiascore[3],yiscore[3]; // 分别表示甲乙胜平负的次数
for(int i = 0 ; i != 3 ; ++i){
jiawin[i] = 0;
yiwin[i] = 0;
jiascore[i] = 0;
yiscore[i] = 0;
}
char SA[2],SB[2];
char A,B;
for (int i = 0 ; i != N ; ++i){
scanf("%s %s",SA,SB); //如果直接scanf("%c %c",&A,&B);会出现错误,具体下面详解
A = SA[0]; B = SB[0];
if (A == B){
++jiascore[TIE];
++yiscore[TIE];
}else if (iswinner(A,B)){
++jiawin[get_index(A)];
++jiascore[WIN];
++yiscore[LOSE];
}else{
++yiwin[get_index(B)];
++jiascore[LOSE];
++yiscore[WIN];
}
}
printf("%d %d %d\n",jiascore[WIN],jiascore[TIE],jiascore[LOSE]);
printf("%d %d %d\n",yiscore[WIN],yiscore[TIE],yiscore[LOSE]);
int maxjia = max(jiawin[CHUI],max(jiawin[JIAN],jiawin[BU]));
int maxyi = max(yiwin[CHUI],max(yiwin[JIAN],yiwin[BU]));
printf("%c %c\n",get_most_win(maxjia,jiawin),get_most_win(maxyi,yiwin));
}
关于scanf输入单个字符的问题
如所见,为了输入空格隔开的两个字符,我们用了字符数组,之后再取字符,为什么要这么干呢?为什么要这么干呢?这是因为scanf输入单个字符时存在一些坑。
#include<stdio.h>
int main(){
char A,B;
while((scanf("%c%c",&A,&B) != EOF)){
printf("A=%c B=%c\n",A,B);
}
return 0;
}
如上所示的代码,会将字符一个接一个地输入A,B之中,其运行结果如下:

而如下所示的代码,会将两个c之间的空格解释成间隔符,认为字符是由空格符隔开的,但是因为前后都没有空格,所以会导致第一个输入被解释为由空格符隔开,之后的输入就不是了。结果如下:
#include<stdio.h>
int main(){
char A,B;
while((scanf("%c %c",&A,&B) != EOF)){ //在两个%c中间多了一个空格
printf("A=%c B=%c\n",A,B);
}
return 0;
}
上面的代码运行结果如下:

最后的两行的输出是因为A最后将再读取最后的回车键,而B没有修改,所以会变成这样。究其原因,就是scanf现在是以”%c %c%c %c%c %c%c %c”这样的匹配模式去匹配的,所以上面输入的字符1,2之间的空格被解释为第一个间隔符,但之后第二个%c和第三个%c之间并没有空格,因此A会匹配到一个空格,接下来的第三个%c和第四个%c虽然是空格隔开的,但在输入里由于2后面的空格后面不是空格,因此scanf会忽略这个间隔符,直接把3读入接下来的B,之后一直循环。在最后一次时,A被填写为换行符,但是B没有被覆盖就遇到了EOF,所以B的值没有被覆盖。
要正确处理类似的输入,下面的代码的行为与预期的相同
#include<stdio.h>
int main(){
char A,B;
while((scanf("%c %c ",&A,&B) != EOF)){ //在两个%c中间多了一个空格,后面也多了一个空格
//while((scanf(" %c %c",&A,&B) != EOF)){ //像这样把空格放在前面也行
printf("A=%c B=%c\n",A,B);
}
return 0;
}
结果如下:

所以关键是将实际的匹配模式修改为”%c %c %c %c %c %c “就行了。
这样其实也不是很保险,所以最好的忽略各种空格的方式,还是例题中写的那样,输入一个%s,之后取其第一个字符。
1019 数字黑洞
x = input()
x = '0' * (4-len(x)) + x #小坑一个,哈哈。
x1 = "".join(sorted(x,reverse = True))
x2 = "".join(sorted(x))
if (x1 == x2):
print("{} - {} = 0000".format(x1,x2))
elif (x1 == "7641"):
print("{} - {} = 6174".format(x1,x2))
else:
while(x != "6174"):
x = str(int(x1) - int(x2))
x = '0' * (4-len(x)) + x #把0补齐
print("{} - {} = {}".format(x1,x2,x))
x1 = "".join(sorted(x,reverse = True))
x2 = "".join(sorted(x))
1020 月饼
from decimal import Decimal
N,D = [int(x) for x in input().split(' ')]
yuebings = [] #其元素也是数组
kucun = [Decimal(x) for x in input().split(' ')]
zongjia = [Decimal(x) for x in input().split(' ')]
for i in range(0,N):
yuebings.append([kucun[i],zongjia[i]])
yuebings.sort(key = lambda x:x[1]/x[0],reverse = True) #按均价排名
xiaoshoue = Decimal(0)
for x in yuebings:
if D >= x[0]:
xiaoshoue += x[1]
D -= x[0]
else:
xiaoshoue += x[1] / x[0] * D
break
print("{:.2f}".format(xiaoshoue))
0 条评论