PTA写BUG日志——数学问题(算法笔记上机实战)

数学问题第一弹

PTA 乙级1003
1003 我要通过! (20 分)

“答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。
得到“答案正确”的条件是:
字符串中必须仅有 P、 A、 T这三种字符,不可以包含其它字符;
任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a、 b、 c 均或者是空字符串,或者是仅由字母 A 组成的字符串。
现在就请你为 PAT 写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。

输入格式:
每个测试输入包含 1 个测试用例。第 1 行给出一个正整数 n (<10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过 100,且不包含空格。

输出格式:
每个字符串的检测结果占一行,如果该字符串可以获得“答案正确”,则输出 YES,否则输出 NO。

输入样例:
9
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA
APT

输出样例:
YES
YES
YES
YES
NO
NO
NO
NO
NO

没有读懂题目就下手,
过了样例的一半,还是误打误撞的那种,嘤~正确答案在下一版本

//1103
#include<bits/stdc++.h>
using namespace std;
int num=0;
bool judge(char ch[],int & num){
	//APATA&&*PAT*
	int p=0,a=0,t=0;
	for(int i=0;i<strlen(ch);i++){
		if(ch[i]!='P'&&ch[i]!='A'&&ch[i]!='T')return false;
		if(p==0&&t==0&&ch[i]=='A')continue;
		if(ch[i]=='P'){
			p++;
			if(p>1)return false;
		}else if(ch[i]=='A'&&p==1&&t==0){
			a++;
			if(a>num+1)return false;
		}else if(ch[i]=='T'){
			if(p==0||a==0)return false;
			t++;
			if(t>1)return false;
		}
	}
	if(a==num+1){
		num++;
		return true;
	}
	if(a==1)return true;
	return false;
		
}
int n;
const int maxn=120;
char a[maxn];
int main(){
	scanf("%d",&n);
	while(n--){
		scanf("%s",&a);
		if(judge(a,num)){
			printf("YES\n");
		}else{
			printf("NO\n");
		}
	}
	
	return 0;
}

修正了一下对于原题的理解,现在可以通过所有的案例啦😊):

  • mid*pre==end的字符数,其中mid\pre\end分别表示开头,pt中间以及pt之后的a/空字符个数
  • 注意必须包含且仅包含pat三个字母
  • 最后注意一下字符长度即可,虽然这里并没有挖坑~

AC代码如下:

//B1003
#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
char str[maxn];
int n;
bool isok(char str[],int len){
	//计算开始、中间、结尾的A的个数
	int pre_a=0,mid_a=0,end_a=0; 
	bool pflag=false,tflag=false;
	for(int i=0;i<len;i++){
		if(str[i]!='P'&&str[i]!='A'&&str[i]!='T'){
			return false;
		}
		if(str[i]=='A'){
			if(!pflag&&!tflag){
				pre_a++;
			}else if(pflag&&!tflag){
				mid_a++;
			}else if(pflag&&tflag){
				end_a++;
			}
		}else if(str[i]=='P'){
			if(pflag==false){
				pflag=true;
			}else return false;//出现了两次P 
		}else if(str[i]=='T'){
			if(pflag==false)return false;//顺序错误
			if(tflag)return false;//出现了两次T 
			else tflag=true; 
		} 
	}
	if(mid_a==0||!tflag||!pflag)return false; 
	if(pre_a==0&&end_a==0)return true;
	if(pre_a*mid_a==end_a)return true;
	return false;
}
int main(){
	scanf("%d",&n);
	while(n--){
		scanf("%s",&str);
		if(isok(str,strlen(str))){
			printf("YES\n");
		}else printf("NO\n");
	}
	
	
	return 0;
} 

数学问题第二弹

PTA乙级1019 甲级1069

这个代码过了一半的样例,emm超时+错误,应该是有部分情况被我漏掉了,还有……解法比较笨拙22333😔

//1019
#include<bits/stdc++.h>
using namespace std;
char num[6];
int num1;
int num2;
int char2num(char num[],int n){
	int sum=0;
	for(int i=0;i<n;i++){
		sum=sum*10+int(num[i]-'0');
	}
	return sum;
}
bool cmp(char a,char b){
	return a>b;
}
void func(char num[],int n){
	char cnum1[7];
	char cnum2[7];
	while(1){
		sort(num,num+n);
		strcpy(cnum1,num);//cnum1小的字符串 
		sort(num,num+n,cmp);
		strcpy(cnum2,num);//cnum2大的字符串 
		num1=char2num(cnum1,n);//num1小的数字 
		num2=char2num(cnum2,n);//num2大的数字
		printf("%04d - %04d = %04d\n",num2,num1,num2-num1);
		if(num2-num1==0||num2-num1==6174)break;
		int temp=num2-num1;
		for(int i=0;i<n;i++){
			num[i]=temp%10+'0';
			temp/=10;
		} 
	}
	
}
int main(){
	scanf("%s",&num);
	func(num,strlen(num));
	
	return 0;
} 

参考一篇博文改进,说实话,这题感觉不应该放在数学问题,应该属于cString库函数的熟练使用

  • stoi->string类型转变为int数字类型
  • str.insert(a,b,‘i’)在字符串str的第[a,b)号位置插入字符’i’
  • to_string(a)将任意字符a转变为string类型
//1019
#include<iostream>
#include<string>
#include<string.h>
#include<stdlib.h> 
#include<algorithm>
using namespace std;
bool cmp(char a,char b){
	if(a>b)return true;
	return false;
}
int main(){
	string num1,num2,ans;
	cin>>ans;
	ans.insert(0,4-ans.length(),'0');//string前面插入字符 
	while(1){
		num1=ans;
		num2=ans;
		sort(num1.begin(),num1.end(),cmp);//num1>num2
		sort(num2.begin(),num2.end());
		int a=stoi(num1);//string->int 
		int b=stoi(num2);
		int c=a-b;
		ans=to_string(c);//int->string 
		ans.insert(0,4-ans.length(),'0');
		cout<<num1<<" - "<<num2<<" = "<<ans<<endl;
		if(c==6174||c==0)break;
	}
	return 0;
} 

数学问题第三弹

PAT 乙级1049 甲级1104

1049 数列的片段和 (20 分)
给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列 { 0.1, 0.2, 0.3, 0.4 },我们有 (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) (0.4) 这 10 个片段。
给定正整数数列,求出全部片段包含的所有的数之和。如本例中 10 个片段总和是 0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0。

输入格式:
输入第一行给出一个不超过 10
5
的正整数 N,表示数列中数的个数,第二行给出 N 个不超过 1.0 的正数,是数列中的数,其间以空格分隔。

输出格式:
在一行中输出该序列所有片段包含的数之和,精确到小数点后 2 位。

输入样例:
4
0.1 0.2 0.3 0.4
输出样例:
5.00

给出1/2的解法,暂时还没有找到BUG,呜呜呜,不清楚哪里错了

//1049
#include<bits/stdc++.h>
using namespace std;
int n;
double num[1801010];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&num[i]);
	}
	double ans=0;
	int right=1,left=n;
	for(int i=1;i<=n;i++){
		ans+=right*left*num[i];
		right++;
		left--;
	}
	printf("%.2f",ans);
	return 0; 
} 

做了以下修改,改正了一个测试点,初步判定是进位的问题

double right=1.0,left=double(n);

//1049
#include<bits/stdc++.h>
using namespace std;
int n;
double num[1801010];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&num[i]);
	}
	double ans=0;
	double right=1.0,left=double(n);
	for(int i=1;i<=n;i++){
		ans+=right*left*num[i];
		right++;
		left--;
	}
	printf("%.2f",ans);
	return 0; 
} 

参考了大佬的代码刷PTA怎么能不提柳神,发现确实是使用double导致精度损失的问题,一个比较经典的解法是先将double类型的✖1000转化为long long类型,累计求和之后再➗1000.0的double类型,进位后保留两位有效数字输出
AC代码如下:

//1049
#include<bits/stdc++.h>
using namespace std;
int n;
typedef long long ll;
ll num[1801010];
double temp;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&temp);
		num[i]=ll(temp*1000);
	}
	ll ans=0;
	
	ll right=1,left=n;
	for(int i=1;i<=n;i++){
		ans+=right*left*num[i];
		right+=1;
		left-=1;
	}
	printf("%.2f",ans/1000.0);
	return 0; 
} 

数学问题第四弹

这一题过于水汪汪,以至于让我怀疑……这个真的是甲级的难度吗?感觉可能就是为了给大家增加自信的题目额
PAT甲级1008

//甲级1008
#include<bits/stdc++.h>
using namespace std;
int num[200];
int n;
int count(int now,int next){
	if(next>now){
		return 5+(next-now)*6;
	}else {
		return 5+(now-next)*4;
	}
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&num[i]);
	}	
	int now=0;
	int sum=0;
	for(int i=0;i<n;i++){
		sum+=count(now,num[i]);//计算当前层到目目标层耗费的时间
		now=num[i]; 
	}
	printf("%d",sum) ;
	return 0;
} 

数学问题第五弹

PAT 甲级1049
暴力代码附上,会有两个样例运行超时😔

//1049
#include<bits/stdc++.h>
using namespace std;
long long int n;
long long int ans=0;
long long int count(long long num){
	long long sum=0;
	while(num){
		if(num%10==1)sum++;
		num/=10;
	}
	//printf("sum=%lld\n",ans);
	return sum;
}
int main(){
	scanf("%lld",&n);
	long long i=1;
	while(i<=n){
		ans+=count(i);
		i++;
	}
	printf("%lld",ans);
	
	return 0;
}

改进后

【择日再补哈~】

数学问题第六弹

PTA乙级1008 数组元素循环右移问题

#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
	scanf("%d %d",&n,&m);
	vector<int>v(n+1);
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
	}
	m=m%n;
	int l=n-m+1;
	bool flag=false;
	for(int i=l;i<=n;i++){
		if(flag==false){
			flag=true;
			printf("%d",v[i]);
			continue;
		}
		printf(" %d",v[i]);
	}
	for(int i=1;i<l;i++){
		if(flag==false){
			flag=true;
			printf("%d",v[i]);
			continue;
		}
		printf(" %d",v[i]);
	}
	return 0;
}

数学问题第七弹

PTA甲级1081 分数加法
第一版:先附上展现思路但是运行没有结果的代码,这段代码没有结果的问题是,输入和处理数据放在同一个循环,在一轮迭代后来不及进行如此庞杂的计算

//1081
#include<bits/stdc++.h>
using namespace std;
//计算 分数的和
//计算最大公因数:辗转相除法
int gcd(int a,int b){
	if(a<b){
		swap(a,b);
	}else{
		if(b==0)return a;
		return gcd(b,a%b);
	}
}
char a[100]; 
//分数的计算
int in=0;
int sum2=0,sum1=0;
int main(){
	int n;
	scanf("%d",&n);
	int temp1,temp2;
	while(n--){
		cin>>a;
		sscanf(a,"%d/%d",&temp2,&temp1);
		int max_in=gcd(temp2,temp1);
		//第一个 
		if(sum1==0){
			sum1=temp1/max_in;
			sum2=temp2/max_in;
			if(sum2/sum1){
				in+=sum2/sum1;
				sum2-=sum1*(sum2/sum1);
			}	
		}
		//第二个
		else{
			temp2=sum2*temp1+sum1*temp2;
			temp1=sum1*temp1;
			max_in=gcd(temp2,temp1);
			sum2=temp2/=max_in;
			sum1=temp1/=max_in;
			if(sum2/sum1){
				in+=sum2/sum1;
				sum2-=sum1*(sum2/sum1);
			}
		} 
	}
	if(in){
		printf("%d %d/%d",in,sum2,sum1);
	}else{
		printf("%d/%d",sum2,sum1);
	}
	return 0;
}

第二版:

  • 修改了输入和数据处理部分,将二者分开;
  • 修改了欧几里得算法,用迭代代替递归后,解决了超时的问题;
    但是仍然有一个样例没有过
    检查
#include<bits/stdc++.h>
using namespace std;
int n;
char a[100];
//辗转相除法 
int gcd(int m,int n){
	if(m>=n)swap(m,n);
	int r=m;
    while(r!=0)
    {
            r=m%n;
            m=n;
            n=r;         
    }
    return m;
}
int in=0,sum2=0,sum1=0;
int maxc;
int main(){
	scanf("%d",&n);	
	vector<int>temp1(n);
	vector<int>temp2(n);
	for(int i=0;i<n;i++){
		cin>>a;
		sscanf(a,"%d/%d",&temp2[i],&temp1[i]);
	}
	for(int i=0;i<n;i++){
		if(i==0){
			maxc=gcd(temp2[i],temp1[1]);
			in+=temp2[i]/temp1[i];
			sum2=temp2[i]/maxc;
			sum1=temp1[i]/maxc;
			sum2-=(sum2/sum1)*sum1;
			continue;
		}else{
			sum2=temp2[i]*sum1+temp1[i]*sum2;
			sum1=temp1[i]*sum1;
			in+=sum2/sum1;
			maxc=gcd(sum1,sum2);
			sum1/=maxc;
			sum2/=maxc;
			sum2-=(sum2/sum1)*sum1;	
		}
	}
	if(in&&sum2){
		printf("%d %d/%d",in,sum2,sum1);
	}else if(sum2){
		printf("%d/%d",sum2,sum1);
	}else if(in){
		printf("%d",in);
	}else{
		printf("0");
	}
	return 0;
}

终于AC啦!感觉自己写的好处就是提高了自己找BUG的能力呢😃

  • 本来gcd函数里加上swap判断是为了减小时间损耗,结果忘记有负数,所以这样写在输入-4/2 4/2会死循环,呜呜呜!
  • 输出的时候需要配注意,整数或者分子部分为0的时候就不输出啦,当然都为零的时候需要输出0
  • 最后复习了一下sscanf()
  • 最后,虽然下面代码可以过样例,但是测试点中没有考虑输出-2/-1 这种情况等等,所以严谨的话还是得改改的,留给大家啦😄
//1081
#include<bits/stdc++.h>
using namespace std;
int sum2=0,sum1=1;//分子、分母
int in;//整数部分
char ch[100];
//获取a,b的最大公因数
int gcd(int a,int b){
	//printf("%d和%d",a,b);
	int temp;
	//if(a<b)swap(a,b);
	while(b!=0){
		temp=b;
		b=a%b;
		a=temp;
	}
	//printf("公因数%d\n",a);
	//4、2 
	return a;
} 
//计算两个分数的加法
void ADD(int a2,int a1){
	//a2分子,a1分母
	int fenzi=sum2*a1+sum1*a2;
	int fenmu=sum1*a1;
	int maxc=gcd(fenzi,fenmu);
	fenzi/=maxc;
	fenmu/=maxc;
	in+=fenzi/fenmu;
	sum1=fenmu;
	sum2=fenzi-(fenzi/fenmu)*fenmu;
} 
int main(){
	int n;
	scanf("%d",&n);
	vector<int>temp2(n);
	vector<int>temp1(n);
	for(int i=0;i<n;i++){
		cin>>ch;
		sscanf(ch,"%d/%d",&temp2[i],&temp1[i]);
	}
	for(int i=0;i<n;i++){
		ADD(temp2[i],temp1[i]);
	}
	
	if(in&&sum2){
		printf("%d %d/%d",in,sum2,sum1);
	}else if(in){
		printf("%d",in);
	}else if(sum2){
		printf("%d/%d",sum2,sum1);
	}else{
		printf("0");
	}
	return 0;
}

数学问题第八弹

PTA甲级1088,分数四则运算进阶
AC😳代码如下
其实做完了上一题,这题反而简单
主要是注意输出格式,俺是踩雷了好几次
最后,给出俺找BUG时候的样例:

输入:-1/5 -1/4

输出
(-1/5) + (-1/4) = (-9/20)
(-1/5) - (-1/4) = 1/20
(-1/5) * (-1/4) = 1/20
(-1/5) / (-1/4) = 4/5

//1088
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char ch1[100];
char ch2[100];
ll gcd(ll a,ll b){
	ll temp;
	while(b!=0){
		temp=b;
		b=a%b;
		a=temp;
	}

	return a;
}
void change(ll fenzi,ll fenmu){
	//格式化
	if(fenmu==0){
		printf("Inf");
		return;
	} 
	ll maxc=gcd(fenzi,fenmu);
	ll n=fenzi/fenmu;
	fenzi/=maxc;
	fenmu/=maxc;
	fenzi%=fenmu;
	if(n<0){
		if(fenzi==0)
			printf("(%lld)",n);
		else{
			printf("(%lld %lld/%lld)",n,abs(fenzi),abs(fenmu));
		}	
	}else if(n==0){
		if(fenzi==0)
			printf("%lld",n);
		else if(fenmu>0&&fenzi>0){
			printf("%lld/%lld",abs(fenzi),abs(fenmu));
		}else{
			printf("(-%lld/%lld)",abs(fenzi),abs(fenmu));
		}	
	}else{
		if(fenzi==0)
			printf("%lld",n);
		else{
			printf("%lld %lld/%lld",n,abs(fenzi),abs(fenmu));
		}	
	}
}
void DO(ll fenzi1,ll fenmu1,ll fenzi2,ll fenmu2){
	//ADD
	change(fenzi1,fenmu1);
	printf(" + ");
	change(fenzi2,fenmu2);
	printf(" = ");
	change(fenzi1*fenmu2+fenzi2*fenmu1,fenmu1*fenmu2);
	printf("\n");
	//SUB
	change(fenzi1,fenmu1);
	printf(" - ");
	change(fenzi2,fenmu2);
	printf(" = ");
	change(fenzi1*fenmu2-fenzi2*fenmu1,fenmu1*fenmu2);
	printf("\n");
	//MUL
	change(fenzi1,fenmu1);
	printf(" * ");
	change(fenzi2,fenmu2);
	printf(" = ");
	change(fenzi1*fenzi2,fenmu1*fenmu2);
	printf("\n");
	//DIV
	change(fenzi1,fenmu1);
	printf(" / ");
	change(fenzi2,fenmu2);
	printf(" = ");
	change(fenzi1*fenmu2,fenmu1*fenzi2);
}
ll fenmu1,fenzi1,fenmu2,fenzi2;
int main(){
	cin>>ch1>>ch2;
	sscanf(ch1,"%lld/%lld",&fenzi1,&fenmu1);
	sscanf(ch2,"%lld/%lld",&fenzi2,&fenmu2);
	DO( fenzi1, fenmu1, fenzi2, fenmu2);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值