PTA - 团体程序设计天梯赛-练习集(更新中)

L1-002 打印沙漏 (20 分)

不算沙漏下面,上面部分随着行数增加,字符的总数依次是1,4,9,16…满足row2×2-1<=n,逆推得上半部分行数(算上1)row=sqrt((n+1)/2),行最大字符数=2×row-1。剩下的for循环实现。

L1-003 个位数统计 (15 分)

用map和数组统计,可以直接char数组,和dodo那题字符类型的通用了(例如按字典序输出一行英文句子每个单词的出现个数)
ps:pta用不了gets,可以改用cin.getline

#include<bits/stdc++.h>
using namespace std;
int main()
{
	char a[1005];
	map<char,int>mp;
	cin.getline(a,1005);
	int len=strlen(a);
	for(int i=0;i<len;++i)  mp[a[i]]++;
	map<char,int>::iterator it;
	for(it=mp.begin();it!=mp.end();it++)
	  cout<<it->first<<":"<<it->second<<endl; 
	return 0;
}

L1-006 连续因子 (20 分)

看数据范围,暴力肯定不行,就一边2~sqrt(n)遍历试根一边求区间并更新起始点和最大区间长度。注意:1不算在序列内,所以要从2开始

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
ll n,len=0,st,maxx=0;
int main()
{
    cin>>n;
    for(ll i=2;i<=sqrt(n);i++)
    {
    	ll x=n,xi=i,len=0;
    	while(x%xi==0)//一旦试根成功就求区间
        {
        	x=x/xi;
        	xi++;
        	len++;
	    }
	    if(len>maxx)  //必须是>  不能是>=
	    { //因为要保证是最长的同时是最小序列,即st要最小
	   	    maxx=len;
	   	    st=i;
	    }
	}
	if(maxx==0)  printf("1\n%lld",n);  //考虑质数情况  序列即本身
	else
	{
	    cout<<maxx<<endl;
	    for(int i=1;i<=maxx;i++)
	    {
	    	if(i==1)  printf("%lld",st);
	    	else printf("*%lld",st);
	    	st++;
    	}
    }
	return 0;
} 

L1-009 N个数求和 (20 分)

因为没开ll白花了半个小时…老经典了。通分 —> 特判t==1 —> 特判和为0 —> 约分 —> 判断符号 —> 分离整数 —> 根据分离后结果处理输出形式。注意:如果和为负数 ,那么之后分离整数时整数和分子都要自带负号

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}
struct Rational 
{
    ll n,d;
}num[105];
ll mod,fz,fm,zs;
int main()
{
    ll t,fh=1;
    cin>>t;
    for(ll i=1;i<=t;i++)  scanf("%lld/%lld",&num[i].n,&num[i].d);
    if(t==1)
    {
        if(num[1].n==0)
        {
            printf("0");
            return 0;
        }
        mod=gcd(abs(num[1].n),abs(num[1].d));
        fz=num[1].n/mod;
        fm=num[1].d/mod;
        fh=fz/abs(fz);
        if(abs(fz)>=fm)
        {
        	fh?zs=fz/fm:zs=-fz/fm;
        	fz=fz%fm;
		}
        if(fz==0)  printf("%lld",zs);
		else  abs(zs)?printf("%lld %lld/%lld",zs,fz,fm):printf("%lld/%lld",fz,fm);
        return 0;
    }    
    for(ll i=1;i<=t-1;i++)
    {
        fm=num[i].d*num[i+1].d;
        fz=num[i].n*num[i+1].d+num[i+1].n*num[i].d;
        num[i+1].n=fz;
        num[i+1].d=fm;
    }
    if(fz==0)
    {
        printf("0");
        return 0;
    }
    mod=gcd(abs(fz),abs(fm));
    fm/=mod;
    fz/=mod;
    fh=(fm/abs(fm))*(fz/abs(fz));
    if(abs(fz)>=fm)
    {
        fh?zs=fz/fm:zs=-fz/fm;
       	fz=fh*abs(fz)%fm;
	}
	if(abs(fz)==0)  printf("%lld",zs);
    else  abs(zs)?printf("%lld %lld/%lld",zs,fz,fm):printf("%lld/%lld",fz,fm);
    return 0;
}

L1-011 A-B (20 分)

因为字符串的每个字符必有ASCII码,所以出现的字符ASCII码范围必在255以内,用vis[]数组初始化为0后遍历b字符串并标记出现过的字符的对应vis值为1,再遍历a字符串,如果对应vis数组的值为0则输出,否则continue。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e4+5;
int vis[255];
int main()
{
	string a,b;
	getline(cin,a);
	getline(cin,b);
	memset(vis,0,sizeof(vis));
	int lena=a.length(),lenb=b.length();
	for(int i=0;i<lenb;i++)  vis[b[i]]=1;
	for(int i=0;i<lena;i++)
	{
		if(vis[a[i]])  continue;
		else  printf("%c",a[i]);
	}
    return 0;
}

L1-017 到底有多二 (15 分)

遍历字符串。注意:倍数是初始值为1,负数则再乘1.5,要是还是偶数则再乘2.0,例如负偶数的倍数=1×1.5×2.0,负奇数的倍数=1×1.5,正偶数的倍数=1×2.0

L1-020 帅到没朋友 (20 分)

k==1时 出现的id或没出现过 的id。用vis数组标记id是否出现过(注意:如果k==1,即朋友圈只有一个人,那么也是属于“没朋友”,无需标记vis数组 ),记得输出后标记出现过,因为可能会有重复查询。几个小细节:①行末空格问题。 如果一边输入一边判断输出,那么如果最终结果只有一个时行末空格就会多出来,所以可以先for循环数 一下“没朋友的人”是否>1,分类讨论输出。②输出id格式问题。 因为必是五位数字,那么如果高位没有数字则要补0,所以输出时格式为**%05d** 。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+5;
int vis[maxn],a[maxn];
int main()
{
	int n;
	scanf("%d",&n);
	memset(vis,0,sizeof(vis));
	int flag=0;
	while(n--)
	{
		int k;
		scanf("%d",&k);
		for(int i=1;i<=k;i++)
		{
			int x;
			scanf("%d",&x);
			if(k!=1) vis[x]=1;
		}
	}
	int m,t=0;
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%d",&x);
		if(vis[x]==0)
		{
            a[++t]=x;
            flag=1;
            vis[x]=1;
		}
	}
    if(t==1)  printf("%05d",a[1]);
    else
    {
        for(int i=1;i<=t;i++)
        {
            printf("%05d",a[i]);
			if(i!=t)  cout<<" ";
        }
    }
	if(flag==0)  printf("No one is handsome");
    return 0;
}

L1-023 输出GPLT (20 分)

①全部转换成大写(transform函数实现)②各个字母计数 ③按序输出直到没有字母剩下
知识点补充: transform函数可以转换string字符串中字母的大小写(数字或其他字符不影响)。

string s;
transform(st,ed,st,::tolower)  //整体转换小写示例

st和ed和pos都是迭代器。

把s字符串[st,ed]区间的子串转换成
小写(tolower)或大写(toupper)

eg.  
s="abcdefg";
transform(s.begin(),s.end(),s.begin(),::toupper);
输出内容为:ABCDEFG
s="abcdefg";
transform(s.begin()+2,s.end()-2,s.begin()+2,::toupper);
输出内容为:abCDEfg

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int main()
{
	string s;
	cin>>s;
	transform(s.begin(),s.end(),s.begin(),::toupper);
	int len=s.length(),gg=0,pp=0,ll=0,tt=0;
	for(int i=0;i<len;i++)
	{
		if(s[i]=='G')  gg++;
		if(s[i]=='P')  pp++;
		if(s[i]=='L')  ll++;
		if(s[i]=='T')  tt++;
	}
	int maxx=max(max(gg,pp),max(ll,tt));
	for(int i=1;i<=maxx;i++)
	{
		if(gg)
		{
			cout<<"G";
			gg--;
		}
		if(pp)
		{
			cout<<"P";
			pp--;
		}
		if(ll)
		{
			cout<<"L";
			ll--;
		}
		if(tt)
		{
			cout<<"T";
			tt--;
		}
	}
    return 0;
}

L1-025 正整数A+B (15 分)

有很多细节要注意,①读取 ②如果是数字还得要求在区间内 ③有一种很坑的情况,例如:【输入:1 1 1】【输出:1 + ? = ?】而不是【1 + 1 = 2】或【? + ? = ?】!!!
我自己的代码很繁琐

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e3+5;
char a[maxn],b[maxn],ch;
int t=1,aa=0,bb=0,flaga=1,flagb=1,x=0,y=0;;
bool judge(char ch)
{
	if(ch>='0'&&ch<='9')  return true;
	else return false;
}
int main()
{
	while(scanf("%c",&ch))
	{
		if(t==1)  
		{
			if((aa==0&&(!judge(ch)||ch=='0'))||(!judge(ch)&&ch!=' '))  flaga=0;
			else if(flaga&&ch!=' ') a[aa++]=ch;
		}
		if(t==2)
		{
			if((bb==0&&!judge(ch))||(!judge(ch)&&ch!=' '&&ch!='\n'))  flagb=0;
			else if(flagb&&ch!='\n') b[bb++]=ch;
		}
		if(ch==' ') t++;
		if(ch=='\n')  break;
	}
	if(t>2)  flagb=0;
	if(flaga==0)  printf("? + ");
	else  
	{
		for(int i=0;i<aa;i++)  x=x*10+a[i]-'0';
		if(x>=1&&x<=1000)  printf("%s + ",a);
		else
		{
			printf("? + ");
			flaga=0;
		}
	}
	if(flagb==0)  printf("? = ");
	else  
	{
	    for(int i=0;i<bb;i++)  y=y*10+b[i]-'0';
		if(y>=1&&y<=1000)  printf("%s = ",b);
		else
		{
			printf("? = ");
			flagb=0;
		}
	}
	if(flaga*flagb==0)  printf("?");
	else	printf("%d",x+y);
    return 0;
}

看到一个比我少一半代码量的码,可以参考一下。要巧用字符串,也要写得干净利落,学习学习。

#include<bits/stdc++.h>
using namespace std;
string a,b;
int checkIt(string str){
    int sum=0;
    for(int i=0;i<str.length();i++){
        if(isdigit(str[i])){
            sum=sum*10+str[i]-'0';
        }else{
            return -1;
        }
    }
    if(sum>=1&&sum<=1000) return sum;
    else return -1;
}
int main(){
    cin>>a;
    getchar();
    getline(cin,b);
    int sumA=checkIt(a);
    int sumB=checkIt(b);
    if(sumA==-1) printf("? + ");
    else printf("%d + ",sumA);
    if(sumB==-1) printf("? = ");
    else printf("%d = ",sumB);
    if(sumA==-1 || sumB==-1) printf("?\n");
    else printf("%d\n",sumA+sumB);
    return 0;
}

L1-028 判断素数 (10 分)

要特判1不是素数。
判断素数函数:

int judge(ll x)
{
    if(x==1)  return 0;
	if(x<=3)  return 1;
	if(x%2==0)  return 0;
	for(ll i=3;i<sqrt(x);i+=2)
		if(x%i==0)  return 0;
	return 1;
}

⭐其他判断素数的更优方法(都是1e9范围):高效率判断素数总结

L1-033 出生年 (15 分)

利用vis数组标记出现过的数字最后累加即为出现过的不同数字个数(要注意高位为0的情况,cnt初始值为1并且后续判断到0也不用变化)。这题写得很快一直卡一个样例,de了十几分钟的bug发现我对题意有误解,y∈[1,3000],但并不意味着x也在这个范围,即x可以大于3000,所以只要把循环终点改为3333即可(这是最大的情况,即y=3000,n=1时)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int vis[11];
int judge(int y,int n)
{
    int cnt=0,yy=y;
    if(y<1000)  cnt++;
	while(y!=0)
	{
		int m=y%10;
		if(m==0&&yy<1000) cnt=cnt;
		else
		{
			cnt+=abs(vis[m]);
            //cout<<yy<<"  "<<m<<"  vis="<<vis[m]<<"  "<<cnt<<endl;
		    vis[m]=0;
		}
		y/=10;
	}
    if(cnt==n)  return 1;
    else return 0;
}
int main()
{
	int y,n;
	cin>>y>>n;
	for(int i=y;i<=3333;i++)
	{
		memset(vis,-1,sizeof(vis));
		if(judge(i,n))
		{
			printf("%d %04d",i-y,i);
			break;
		}
	}
    return 0;
}

L1-044 稳赢 (15 分)

尝试后发现自定义函数返回值类型还能选string的😁,越来越喜欢用string了,好香。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e4+5;
int k;
string win(char ch)
{
	if(ch=='C')  return "Bu";
	else if(ch=='J')  return "ChuiZi";
	else if(ch=='B')  return "JianDao";
}
int main()
{
	cin>>k;
	string s;
	int t=0;
	while(cin>>s)
	{
		if(s[0]=='E')  break;
		if(t==k)
		{
			cout<<s<<endl;
			t=0;
		} 
		else  
		{
			cout<<win(s[0])<<endl;
			t++;
		}
	}
    return 0;
}

L1-046 整除光棍 (20 分)

因为求出来的数可能很大,所以不可能是通过s来求光棍数。那么就通过光棍数逐位增加的同时来一位位地求出s。其实相当于摆竖式的原理,从高位开始运算,除不尽就多加一位1,循环往复,直到正好整除为止。图片

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int x,s,n=0;
int main()
{
	scanf("%d",&x);
	int num=0,flag=0;
	for(int i=1;;i++)
	{
		num=num*10+1;
		if(num>=x)
		{
			s=num/x;
			printf("%d",s);
			flag=1;
		}
		else if(flag) printf("0");
		num=num%x;
		if(num==0)  
		{
			printf(" %d",i);
			break;
		}
	}
	return 0;
 } 

L1-054 福到了 (15 分)

正序赋值后倒序输出,要注意getchar()的几个地方。模拟一下就知道满足倒序的下标对应的是: a [ i ] [ j ] a[ i ] [ j ] a[i][j] —> a [ n − i + 1 ] [ n − j + 1 ] a[ n-i+1 ] [ n-j+1 ] a[ni+1][nj+1] 。关于判断需不需要倒的话我是又遍历了一遍,可能有点麻烦,有好方法欢迎评论。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e4+5;
char a[105][105];
int main()
{
	char ch;
	int n;
	cin>>ch>>n;
	getchar();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			a[i][j]=getchar();
			if(a[i][j]!=' ') a[i][j]=ch;
		}
		getchar();
	}
	int flag=1;
	for(int i=1;i<=n&&flag;i++)  //判断需不需要倒 
	{
		for(int j=1;j<=n;j++)
		{
			if(a[i][j]!=a[n-i+1][n-j+1])
			{//一旦发现正反不同就标记并break
				flag=0;
				break;
			}
		}
	}
	if(flag)  cout<<"bu yong dao le"<<endl;
	for(int i=n;i>=1;i--)  //倒序输出
	{
		for(int j=n;j>=1;j--)  cout<<a[i][j];
		cout<<endl;
	 } 
    return 0;
}

L1-058 6翻了 (15 分)

s t r i n g string string e r a s e erase erase 函数来实现替换(删除后再跟加)。有一些细枝末节的地方要写得仔细一点,比如判断是否为 6 6 6 的起始位置或 6 6 6 的连续区间、 x i xi xi b b b 字符串中的位置坐标所以不能直接记为 i i i 而应该是重新计算 b b b 的当前长度、以及删除时的区间坐标,最后几行有一句注释掉的可以用来debug,会清楚很多。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e4+5;
int main()
{
	string a,b;
	getline(cin,a);
	int len=a.length(),t=0,flag=0,xi;
	for(int i=0;i<=len;i++)
	{
		if(a[i]=='6')
		{
			if(t==0)  //检测到起始情况
			{
				flag=1;
			    b+=a[i];
			    t++;
			    xi=b.length()-1;  //记录6的起始位置 
			}
			else if(flag)  //当前为连续区间
			{
				b+=a[i];
				t++;
			}
		}  
		else if(a[i]!='6')
		{
			if(flag)
			{
			    flag=0;  //记得初始化
			    if(t>3&&t<=9)  //替换
			    {
				    b.erase(xi,xi+t);
				    b+='9';
			    }
			    else if(t>9)  //替换
			    {
			    	b.erase(xi,xi+t);
			    	b+="27";
			    }
		    }
			b+=a[i];  //不要忘记跟加这个不为6的字符
			t=0;  //不要忘记初始化
		} 
		//cout<<"i="<<i<<"  t="<<t<<"  xi="<<xi<<"  b="<<b<<endl;
	}
	cout<<b;
    return 0;
}

L1-069 胎压监测 (15 分)

求最大值可以用max_element函数 ,例如a[0]~a[3]最大值 m a x x maxx maxx = a[ max_element( a , a+4 ) - a ]。

L1-064 估值一亿的AI核心代码 (20 分)

这题有点绕,要保持头脑清醒一点,合理安排好执行每种操作的先后顺序,剩下的就是基础问题和细节问题
① 去除空格(erase函数和isalnum函数实现)
②转换小写(需要特判独立的 “ I ” “I” I 的情况)
③替换“can you”和“could you”(find函数和replace函数实现)。这里要注意一个问题,不能直接换成“I can/could”,因为后面还要替换“I/me”这一类,会重复误操作。
④替换“I/me”(同理③)
⑤替换“?”和③中的人称
部分知识点补充:
①erase函数的三种用法(参考博客:传送门

string s;
s.erase(i,n);  删除第i个开始的n个字符
s.erase(it);  删除迭代器it所指向的字符(即第it个字符)
s.erase(s.begin()+st,s.end()-ed);  保留前st个和后ed个,其余中间的删除

②isalnum函数(参考博客:传送门
int isalnum(int x) 判断x是否为字母或数字,是就返回1,否就返回0。
③find函数(参考博客:①find详解find避坑

string s,str;
s.find(str);  在s字符串中查找str字符串,返回第一次找到的下标,找不到返回-1;
s.find(str,i);  在s字符串中第i个位置开始查找str字符串,返回第一次找到的下标,找不到返回-1;
s.find(str,i,j);  在s字符串中第i个位置开始查找str字符串的前j个子串,返回第一次找到的下标,找不到返回-1

④replace函数(参考博客:传送门

string s,str;
s.replace(i,len,str);  替换s字符串第i个位置开始长为len的子串为str;
s.replace(st,ed,str);  类似于上一种,但st和ed都是迭代器;
s.replace(i,len,n,ch);  替换s字符串第i个位置开始长为len的子串为n个ch;
s.replace(st,ed,n,ch);  替换s字符串区间为[st,ed]的子串为n个ch;(st和ed为迭代器)
s.replace(i,len1,str,j,len2);  将s的位置为[i,i+len1-1]的子串替换为str的位置为[j,j+len2-1]的子串;

AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e4+5;
int main()
{
	int n;
	cin>>n;
	getchar();
	while(n--)
	{
		string s,a;
		getline(cin,s);
		cout<<s<<endl<<"AI: ";
		//transform(s.begin(),s.end(),s.begin(),::tolower);
		while(s[0]==' ')  s.erase(s.begin());  //开头多余空格 
		while(s[ s.length()-1 ]==' ')  s.erase(s.end()-1);  //结尾多余空格
		for(int i=0;i<s.length();i++)
		{
			if(s[i]==' ')   //去除中间多余空格 
			{
				while(s[i+1]==' ')  s.erase(s.begin()+i+1);
				if(!isalnum(s[i+1]))  s.erase(s.begin()+i);
			}
		}
		for(int i=0;i<s.length();i++)  //转换小写 
		    if(s[i]>='A'&&s[i]<='Z'&&s[i]!='I')  s[i]+=32;
		for(int i=0;;i++)  //替换can 
		{
			i=s.find("can you",i);
			if(i==-1)  break;
			if( ( !i || !isalnum(s[i-1]) )&&( i+7==s.length() || !isalnum(s[i+7]) ) ) 
			   s.replace(i,7,"Q can");
		} 
		for(int i=0;;i++)  //替换could 
		{
			i=s.find("could you",i);
			if(i==-1)  break;
			if( ( !i || !isalnum(s[i-1]) )&&( i+9==s.length() || !isalnum(s[i+9]) ) ) 
			   s.replace(i,9,"Q could");
		} 
		for(int i=0;;i++)   //替换I
		{
			i=s.find("I",i);
			if(i==-1)  break;
			if( ( !i || !isalnum(s[i-1]) )&&( i+1==s.length() || !isalnum(s[i+1]) ) ) 
			   s.replace(i,1,"you");
		}
		for(int i=0;;i++)   //替换me
		{
			i=s.find("me",i);
			if(i==-1)  break;
			if( ( !i || !isalnum(s[i-1]) )&&( i+2==s.length() || !isalnum(s[i+2]) ) ) 
			   s.replace(i,2,"you");
		}
		for(int i=0;i<s.length();i++)
		{
			if(s[i]=='?')  s[i]='!';
			if(s[i]=='Q')  s[i]='I';
		}
		cout<<s<<endl;
	}
    return 0;
}

L2-032 彩虹瓶 (25 分)

用堆栈模拟一下就行,注意一下now==num后栈顶元素可不可以继续取走,直到没有可取的为止(while实现)。这题模拟赛做到了,很有思路但是因为基础很差,堆栈的一些基本操作忘了,赛后看了眼笔记就过了,所以还是基础要补好,毕竟不能带纸质材料。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int maxn=1e5+5;
int n,m,k;
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	while(k--)
	{
		stack<int>st;
		int num=1,now,flag=1;
	    for(int i=1;i<=n;i++)
	    {
		    scanf("%d",&now);
		    if(now!=num) 
			{
				st.push(now);
				if(st.size()>m)  flag=0;
			}
			else
			{
				num++;
				while(!st.empty())
				{
					if(st.top()==num)
					{
						num++;
		    	        st.pop();
					}
					else break;
				}
			}
	    }
		if(flag!=0&&st.empty())  printf("YES\n");
		else printf("NO\n");
	}
	return 0;
 } 
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值