PAT (Basic Level) Practice (中文)入门篇(5)C

1040 有几个PAT (25分)

字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位( P),第 4 位(A),第 6 位(T);第二个 PAT 是第 3 位( P),第 4 位(A),第 6 位(T)。

现给定字符串,问一共可以形成多少个 PAT?

输入格式:
输入只有一行,包含一个字符串,长度不超过10^5​​ ,只包含 P、A、T 三种字母。

输出格式:
在一行中输出给定字符串中包含多少个 PAT。由于结果可能比较大,只输出对 1000000007 取余数的结果。

输入样例:

APPAPT    

输出样例:

2

原题链接
思考

  • 直接用暴力会超时,所以……
  • 换个角度思考问题,对一个确定位置的A来说,以它形成的PAT的个数等于它左边P的个数乘以它右边T的个数。
  • 例如对字符串APPAPT的中间那个A来说,它左边有两个P,右边有一个T,因此这个A能形成的PAT的个数就是2x1=2。
  • 于是问题就转换为,对字符串中的每个A,计算它左边P的个数与它右边T的个数的乘积,
  • 然后把所有A的这个乘积相加就是答案。
  • 所以,只需要设定一个数组left,记录每一位左边P的个数(含当前位,下同)。
  • 接着从左到右遍历字符串,如果当前位i是P,那么left[i]就等于left[i-_1]加1:
  • 如果当前位i不是P,那么left[i]就等于left[i-L1]1。
  • 于是只需要O(len)的时间复杂度就能统计出left数组。
  • 以同样的方法可以计算出每一位右边T的个数。
  • 为了节省代码量,不妨在统计每一位右边T的个数的过程中直接计算答案sum,具体做法是:
  • 定义一个变量right,记录当前累计右边T的个数。
  • 从右往左遍历字符串,如果当前位i是T,那么令right加1;
  • 否则,如果当前位1是A,那么令sum加上left[i]与right的乘积(注意取模)。
  • 这样,当遍历完字符串时,就得到了答案sum。

注意点

  1. 采用分别遍历P、A、T的位置来统计的方法会超时。
  2. 记得取模。
  3. 本题与PATB1045/A1101的思路很像,注意认真体会这两道题的思想。

AC1

#include <stdio.h>
int main(){
	char str[100010];
	int i,left[100010]={0};	//每一位左边含P的个数
	gets(str);	 //读入字符串 
	int len=strlen(str); //长度
	for(i=0;i<len;i++){
		if(i>0)	left[i]=left[i-1]; //继承上一位的位置
		if(str[i]=='P')	left[i]++; 
	} 
	int sum=0,right=0; //sum为答案,right记录右边T的个数 
	for(i=len-1;i>=0;i--){
		if(str[i]=='T'){
			right++;
		} else if(str[i]=='A'){
			sum=(sum+left[i]*right)%1000000007; //累计乘积 
		}
	}
	printf("%d\n",sum); 
	return 0;
}

AC2

#include <stdio.h>
int main(){
    int LIM=1000000007;
    int P=0,PA=0,PAT=0;
    char c;
    while((c=getchar())!= '\n'){
        if(c=='P')  P++;
        if(c=='A')  PA=(PA+P)%LIM;
        if(c=='T')  PAT=(PAT+PA)%LIM;
    }
    printf("%d",PAT);
    return 0;
}

1019 数字黑洞 (20分)

给定任一个各位数字不完全相同的 4 位正整数,如果我们先把 4 个数字按非递增排序,再按非递减排序,然后用第 1 个数字减第 2 个数字,将得到一个新的数字。一直重复这样做,我们很快会停在有“数字黑洞”之称的 6174,这个神奇的数字也叫 Kaprekar 常数。

扫描二维码关注公众号,回复: 10080302 查看本文章

例如,我们从6767开始,将得到

7766 - 6677 = 1089
9810 - 0189 = 9621
9621 - 1269 = 8352
8532 - 2358 = 6174
7641 - 1467 = 6174
... ...

现给定任意 4 位正整数,请编写程序演示到达黑洞的过程。

输入格式:
输入给出一个 (0,10^​4) 区间内的正整数 N。

输出格式:
如果 N 的 4 位数字全相等,则在一行内输出 N - N = 0000;否则将计算的每一步在一行内输出,直到 6174 作为差出现,输出格式见样例。注意每个数字按 4 位数格式输出。

输入样例 1:

6767

输出样例 1:

7766 - 6677 = 1089
9810 - 0189 = 9621
9621 - 1269 = 8352
8532 - 2358 = 6174

输入样例 2:

2222

输出样例 2:

2222 - 2222 = 0000

原题链接
思考

  • 步骤1:写两个函数:int型整数转换成int型数组的toarray函数(即把每一位都当成数组的一个元素)、int型数组转换成int型整数的tonumber函数。
  • 步骤2,建立一个while循环,对每一层循环:
  1. 用to_array函数将n转换为数组并递增排序,再用to_number函数将递增排序完的数组转换为整数MIN。
  2. 将数组递减排序,再用tonumber函数将递减排序完的数组转换为整数MAX。
  3. 令n=MAX-MIN为下一个数,并输出当前层的信息。
  4. 如果得到的n为0或6174,退出循环。

注意点

  • 如果采用其他写法,容易发生问题的是6174这个数据可能没有输出,实际上应该输出:7641-1467=6174
  • 如果某步得到了不足4位的数,则视为在高位补0,如189即为0189。

AC1——C++

#include <cstdio>
#include <algorithm>
using namespace std;
int i;
bool cmp(int a,int b){ //递减排序 
	return a>b;
}
void to_array(int n,int num[]){	//将n的每一位存到num数组中 
	for(i=0;i<4;i++){
		num[i]=n%10;
		n/=10;
	}
} 
int to_number(int num[]){	//将num数组转换为数字 
	int sum=0;
	for(i=0;i<4;i++){
		sum=sum*10+num[i];
	} 
	return sum;
}
int main(){
	int n,MAX,MIN,str[5];
	scanf("%d",&n);
	while(1){
		to_array(n,str);	//将n转换为数组
		sort(str,str+4);	//对num数组中元素从小到大排序
		MIN=to_number(str);	//获取最小值
		sort(str,str+4,cmp);	//对num数组中元素从大到小排序 
		MAX=to_number(str);	//获取最大值 
		n=MAX-MIN;	//得到下一个数 
		printf("%04d - %04d = %04d\n",MAX,MIN,n);	//注意格式 
		if(n==0||n==6174)	break; //如果下个数是0或6174则退出 
	}
	return 0;
}

AC2——C

#include <stdio.h>
int cap(const void *a, const void *b){
    return *(int*)b - *(int*)a;
}
int stor(int n){
    int zoo[4] = {n/1000, n%1000/100, n%100/10, n%10};
    qsort(zoo, 4, sizeof(int), cap);
    return zoo[0] * 1000 + zoo[1] * 100 + zoo[2] * 10 + zoo[3];
}
int rever(int n){
    return n/1000 + n%1000/100 * 10 + n%100/10 * 100 + n%10 * 1000;
}
int main(){
    int n;
    scanf("%d", &n);
    do{
        n = stor(n);
        printf("%04d - %04d = %04d\n", n, rever(n), n - rever(n));
        n = n - rever(n);
    }while(n != 0 && n != 6174) ;
    return 0;
}

1013 数素数 (20分)

令 P​i表示第 i 个素数。现任给两个正整数 M≤N≤10^4,请输出P​M到P​N的所有素数。

输入格式:
输入在一行中给出 M 和 N,其间以空格分隔。

输出格式:
输出从 P​M 到 P​N的所有素数,每 10 个数字占 1 行,其间以空格分隔,但行末不得有多余空格。

输入样例:

5 27

输出样例:

11 13 17 19 23 29 31 37 41 43
47 53 59 61 67 71 73 79 83 89
97 101 103

原题链接
思考

  • 先给出两个正整数 M,N,输出第 M 个素数到第 N 个素数之间的所有素数,M<=N<=10^4
  • 把素数表打至第N个素数,按格式输出。

注意

  1. 用筛法或者非筛法都可以解决该题,在算法中只需要添加一句控制素数个数的语可:
    if(pNum>=n)	break;
    
    这是由于题目只要求输出第m~n个素数,因此超过n个素数之后的就不用保存了。
  2. 小技巧:由于空格在测试时肉眼看不出来,因此如果提交返回“格式错误“,读者可以把程序中的空格改成其他符号(比如#)来输出,看看是哪里多了空格。
  3. 考虑到不知道第10^4个素数有多大,不妨将测试上限maxn设置得大一些,反正在素数个数超过n时是会中断的,不影响复杂度.当然也可以先用程序测试下第10 ^4个素数是多少,然后再用这个数作为上限。
  4. 本题在素数表生成过程中其实就可以直接输出,不过看起来会显得比较冗乱,因此还是应先生成完整素数表,然后再按格式要求输出。
  5. Find_Prime0函数中要记得是i<maxn而不是i<=maxn,否则程序一运行就会崩溃;在main函数中要记得调用Find_Prime0)函数,否则不会出结果。
#include <stdio.h>
typedef int bool;	//自定义bool 
#define false 0
#define true  1
int prime[1000001],pNum=0;	//prime数组存放数组,pNum为素数个数 
bool p[1000001]={0};	//如果i为素数,p[i]为false;否则为true 
void Find_Prime(int n){
	int i,j;
	for(i=2;i<1000001;i++){
		if(p[i]==false){
			prime[pNum++] = i;
			if(pNum >= n)	break;	//只需要 n 个素数,超过即可结束 ,核心!!
			for(j=i+i;j<1000001;j+=i){
				p[j]=true;
			}
		}
	}
}
int main(){
	int M,N,i,count=0;
	scanf("%d %d",&M,&N); 
	Find_Prime(N);
	for(i=M;i<=N;i++){
		printf("%d",prime[i-1]);	//下标从 0 开始
		count++; 
		if(count%10!=0 && i<N)	printf(" ");	//每行10个,行末无多余空格 
		else	printf("\n");
	}
	return 0;
}  

1037 在霍格沃茨找零钱 (20分)

如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 —— 就如海格告诉哈利的:“十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易。”现在,给定哈利应付的价钱 P 和他实付的钱 A,你的任务是写一个程序来计算他应该被找的零钱。

输入格式:
输入在 1 行中分别给出 P 和 A,格式为 Galleon.Sickle.Knut,其间用 1 个空格分隔。这里 Galleon 是 [0, 10^​7] 区间内的整数,Sickle 是 [0, 17) 区间内的整数,Knut 是 [0, 29) 区间内的整数。

输出格式:
在一行中用与输入同样的格式输出哈利应该被找的零钱。如果他没带够钱,那么输出的应该是负数。

输入样例 1:

10.16.27 14.1.28

输出样例 1:

3.2.1

输入样例 2:

14.1.28 10.16.27

输出样例 2:

-3.2.1

原题链接
样例解释

  • 样例1

    • 就Knut来说,需要找钱28-27=1;就Sickle来说,1-16不够减,于是兑换1个Galleon为17个Sickle,这样Sickle就变成1+17=18个,需要找钱18-16=2;就Galleon来说,由于之前被兑换了1个,因此只剩下13个,需要找钱13-10=3。
  • 样例2

    • 在样例1的基础上加个负号即可。

思考

  • 根据题意可知,1个Galleon可以兑换17x29个Knut,1个Sickle可以兑换29个Knut,因此直接把货币全部转换成Knut来计算。
  • 于是第二个减去第一个即可得到要找的钱,假设为K。
  • 由于此时单位是Knut,因此若要转换为原来的格式,就有
  • K/(17x29)个Galleon,K%(17x29)129个Sickle,K%29个Knut。

注意点

  • 获得Knut为单位的找零的钱K后要将它取绝对值,不能直接把负数直接代入后面的运算。

AC

#include <stdio.h>
int main() {
    int Galleon, Sickle, Knut, P, A, change; 
    scanf("%d.%d.%d", &Galleon, &Sickle, &Knut);
    P = (Galleon * 17 + Sickle) * 29 + Knut; //价钱 
    scanf("%d.%d.%d", &Galleon, &Sickle, &Knut);
    A = (Galleon * 17 + Sickle) * 29 + Knut; //实付 
    change = A - P; 
    if (change < 0) {//钱不够 
        printf("-");
        change = -change;
    }
    printf("%d.%d.%d\n", change / 29 / 17, change / 29 % 17, change % 29);
    return 0;
}

1006 换个格式输出整数 (15分)

让我们用字母 B 来表示“百”、字母 S 表示“十”,用 12…n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过 3 位的正整数。例如 234 应该被输出为 BBSSS1234,因为它有 2 个“百”、3 个“十”、以及个位的 4。

输入格式:
每个测试输入包含 1 个测试用例,给出正整数 n(<1000)。

输出格式:
每个测试用例的输出占一行,用规定的格式输出 n。

输入样例 1:

234

输出样例 1:

BBSSS1234

输入样例 2:

23

输出样例 2:

SS123

原题链接
思考

  • 步骤1:考虑到需要从高位到低位进行枚举,因此不妨先将n的每一位存到数组中。
  • 令int型ans数组存放h的每一位,num表示n的位数。
  • 步骤2:对ans数组从高位到低位枚举:
  • 如果是百位,则输出该位数字的个数的‘B’。
  • 如果是十位,则输出该位数守的个数的‘S’。
  • 如果是个位,则输出从1到该位数字。

AC1

#include <stdio.h>
int main(){
	int n,i,j;
	scanf("%d",&n);
	int num=0,ans[5];
	while(n!=0){	//将n的每一位存入ans数组 
		ans[num]=n%10;
		num++;
		n=n/10;
	}
	for(i=num-1;i>=0;i--){	//从高位到低位输出 
		if(i==2){	//如果是百位 
			for(j=0;j<ans[i];j++){
				printf("B");	//输出ans[i]个B 
			}
		} else if(i==1){
			for(j=0;j<ans[i];j++){
				printf("S");	//输出ans[i]个S
			} 
		} else{	//如果是个位 
			for(j=1;j<=ans[i];j++){
				printf("%d",j);
			}
		}
	}
	return 0;
}

AC2

#include<stdio.h> 
int main(){   
    int N,i;
    scanf("%d",&N);
    int B = N/100;	//存储百位
    if(B != 0) {	//输出B个B
        for(i = 0;i < B;i++) printf("B"); 
    }
    int S = N % 100 / 10;	//存储十位
    if(S != 0) {
        for(i = 0;i < S;i++) printf("S"); 
    } 
    int G = N % 10;	//存储个位
    for(i=1;i<=G;i++) printf("%d",i);
    return 0;
 } 
发布了32 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_46153949/article/details/104217064