【算法】2023移动应用开发实验室二面题题解

本文解析了2023年移动应用开发实验室二面题中的几个部分,涉及算法优化(素数回文数计算)、字符串函数、高精度模拟(如减法)及C语言字符串操作等,提供了解题思路和技巧。

2023移动应用开发实验室二面题题解

笔者:吃汉堡吃到饱

本次二面题兼顾语法知识的考察和一部分算法知识的考察,可挖掘之处甚多,如素数回文数计算时如何优化?开关灯的数学解法?字符串函数如何使用?高精度模拟算法如何实现?本篇博客记录其中一部分题解以及部分答案,无法做到尽善尽美,但会在日后努力补齐其短板。

1.小鱼比可爱

原题链接:

P1428 小鱼比可爱 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

请添加图片描述

int main()
{
    int n, count = 0;
    scanf("%d", &n);
    int arr[101] = { 0 };
    int arr_lovely[101] = { 0 };
    for (int i = 0; i < n; i++)scanf("%d", &arr[i]);
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < i; j++)
        {
            if (arr[j] < arr[i])
            {
                arr_lovely[i]++;
            }
        }
    }
    for (int i = 0; i < n; i++)
        printf("%d ",arr_lovely[i]);
return 0;
}

没啥好说的,先这样再那样。(bushi)

用数组下标模拟排成一排的小鱼,而后用嵌套循环分别遍历小鱼,以及某一小鱼前面的所有小鱼,查找到符合要求的小鱼的数量,再加起来,存入数组中。

2.素数回文数的个数

原题链接:

B2136 素数回文数的个数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

请添加图片描述

int Palindrome(int o)
{
	int x, y;
	int z = 0;
	x = o;
	while (o != 0)
	{
		y = o % 10;
		z = z * 10 + y;
		o /= 10;
	}
	return x == z;
}

int Prime(int num)
{
	if (num == 2) return 1;
	if (num % 2 == 0) return 0;//2是唯一的偶素数,这样可以减少运行时间
	for (int i = 2; i <= sqrt(num); i++)
	{
		if (num % i == 0)
			return 0;
	}
	return 1;
}

int main()
{
	int count = 0;
	int n = 0;
	scanf("%d", &n);
	for (int i = 11; i <= n; i += 2)
	{
		if (Palindrome(i) == 1)
		{
			if (Prime(i) == 1)
			{
				count++;
			}
		}
	}
	printf("%d", count);
	return 0;
}

本题考察回文数和质数的计算,单纯计算这两者并不难,但我此前做过一道类似的题目,觉得还可以就此说道说道。

此处放上原题链接:

[P1217 USACO1.5] 回文质数 Prime Palindromes - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题与本题一样,统计某一区间内质数回文数,仅有一处不同:范围极大,这就会导致两个问题:

1.仅仅用if语句判断,用除法翻转然后判断的方法行不通。

2.不进行计算上的优化,会导致超时。

解决方法如下:

1.回文数判断

对于第一点,选择用一个函数将原有数字翻转,而后进行判断。

2.超时问题

1.对全局判断流程:

显然,回文数数量远少于质数,故而可以先判断回文数,而后再找出回文数中的质数,这样可以大大减少循环次数。

2.对判断素数的局部流程:

1.如果num为2,则直接返回真,除二以外的偶数,全部返回假。

2.循环语句中,仅仅需要循环至num的平方根即可。

int Prime(int num)
{
	if (num == 2) return 1;
	if (num % 2 == 0) return 0;//2是唯一的偶素数,这样可以减少运行时间
	for (int i = 2; i <= sqrt(num); i++)
	{
		if (num % i == 0)
			return 0;
	}
	return 1;
}

3.开关灯

原题链接:

B2092 开关灯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

请添加图片描述

1.样本推理法

特别鸣谢:

愿相知_-CSDN博客提供的特别解法,受益匪浅

#include<stdio.h>
int main()
{
	int n,i;
	scanf("%d",&n);
	for(i=1;i*i<=n;i++)
	{
		printf("%d ",i*i);
	 } 
}

对写下的样本进行数学上的推理,可发现仅有平方数为亮,其余为暗。

(解释是:一盏灯若对其操作偶数次,则为暗,若对其操作奇数次,则为亮,而平方数因为因数的重叠导致比其他数字多操作了一次,故而为奇数次操作,即为亮。)

2.数组模拟法

#include <stdio.h>
int main() {
    // 创建一个长度为5000的数组,用来记录每个灯的状态
    int lights[5000] = { 0 };
    int n;
    scanf("%d", &n);
    // 模拟开关操作
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (j % i == 0) {
                // 灯的状态取反
                lights[j] = 1-lights[j];
            }
        }
    }
    // 统计最终开着的灯
    for (int i = 1; i <= n; i++)
    {
        if (lights[i] == 1)
            printf("%d ", i);
    }
    return 0;
}

用数组下标模拟灯的状态,外层循环计数开关灯的编号,内层循环则负责遍历灯的队列。

4.确定进制

原题链接:

B2141 确定进制 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

请添加图片描述

#include<stdio.h>
#include<math.h>
//十进制转换
long long int Convert(long long int a, long long int i)
{
	long long int sjz = 0;
	long long int count = 0;
	long long int temp = a;
	while (temp != 0)
	{
		if (temp % 10 >= i)
			return -1;
		temp /= 10;
	}
    //逐位判断是否符合进制要求,若某一位数字大于进制数字,则不符合要求,返回-1
	while (a != 0)
	{
		sjz += (a % 10) * pow(i, count);
		a /= 10;
		count++;
	}
    //将X进制转换为十进制
	return sjz;
}
int main()
{
	long long int p, q, r;
	scanf("%lld %lld %lld", &p, &q, &r);
	long long int i = 2;
	for (i = 2; i <= 16; i++)
	{
		long long int judge = 0;
		long long int p2 = p, q2 = q, r2 = r;
		//将三个数转换为十进制
		long long int p1 = Convert(p, i);
		long long int q1 = Convert(q, i);
		long long int r1 = Convert(r, i);
        //判断进制是否符合要求
		if (p1 == -1 || q1 == -1 || r1 == -1)
			continue;
		if (p1 * q1 == r1)
			break;
	}
	if (i == 17)
		printf("0");
	else
		printf("%lld", i);
	return 0;
}

5.拼数

原题链接:

[P1012 NOIP1998 提高组] 拼数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述:

请添加图片描述

#include<stdio.h>
#include<string.h>
//防止栈溢出,选择在静态区建立字符串大数组
char str[10000][10000];
int main()
{
	//一、输入部分
	int n = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%s", str[i]);
	//二、字符串数组进行排序
for (int i = 0; i < n; i++)
{
    char cmp1[1000] = { 0 }, cmp2[1000] = { 0 };
    for (int j = i; j < n; j++)
    {
        strcpy(cmp1,str[i]);
        strcat(cmp1, str[j]);
        strcpy(cmp2, str[j]);
        strcat(cmp2, str[i]);
        if (strcmp(cmp1, cmp2) < 0)
        {
            char temp[1000] = {0};
            strcpy(temp, str[i]);
            strcpy(str[i], str[j]);
            trcpy(str[j], temp);
        }
    }
}

    //三、输出部分
	for (int i = 0; i < n; i++)
		printf("%s", str[i]);
	return 0;
}

针对此题写了一篇博客:

借拼数窥见C语言字符串操作的一角-CSDN博客

6.高精度减法

原题链接:

P2142 高精度减法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

请添加图片描述

代码部分借鉴:

(C语言)高精度减法_高精度减法c语言-CSDN博客

#include<stdio.h>
#include<string.h>
//静态区创建大数组,防止栈溢出
char a[100000], b[100000];
int A[100000], B[100000];

void Subtraction() 
{
	int l1 = strlen(a), l2 = strlen(b), len;
	int judge = 0;//用于判断哪一字符串更大
	int i = 0;
	if (l2 > l1 || (strcmp(a, b) < 0 && l1 == l2)) 
        //仅仅用字符串比较函数并不能判断出孰大孰小,再加入字符串长度的比较
	{
		judge = 1;
		for (i = l2 - 1; i >= 0; i--)
			A[l2 - i - 1] = b[i] - '0';
		for (i = l1 - 1; i >= 0; i--)
			B[l1 - i - 1] = a[i] - '0';
	}
	else 
	{
		for (i = l1 - 1; i >= 0; i--)
			A[l1 - i - 1] = a[i] - '0';
		for (i = l2 - 1; i >= 0; i--)
			B[l2 - i - 1] = b[i] - '0';
	}
    //输入部分,将字符串数组的元素一个个转换为数字,而后存入数组内
	if (l1 > l2)
		len = l1;
	else
		len = l2;
	for (i = 0; i < len; i++) 
    {
		A[i] = A[i] - B[i];
		if (A[i] < 0) 
        {
			A[i + 1] -= 1;
			A[i] += 10;
		}
	}//模拟减法以及减法的借位
	while (A[len - 1] == 0 && len > 1)
		len--;//去除前导零
	if (judge)
		printf("-");
	for (i = len - 1; i >= 0; i--)
		printf("%d", A[i]);//倒序输出(从高位到低位输出
}

int main()
{
	//输入部分,用getchar取走字符串之间的换行符
	scanf("%s", a);
	getchar();
	scanf("%s", b);
	Subtraction();
	return 0;
}

高精度算法在C语言中弥足轻重,由于C语言的整形类型能存储的数值范围有限,若需要存储非常大的数,则可以选择使用高精度算法。

高精度算法实则为一种模拟算法,模拟竖式进行计算,通过借位以及进位的方式在数组中用逐个元素的加减实现整个大数字的加减。之后会单独写一篇关于高精度算法的博客。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值