这次让我们来看看总让人头疼的数数数的问题🤣🤣
一 大数处理之回文串😎
(撰稿人 brainstorm YYF)
题目描述
回文数是从前往后和从后往前得到的数是相同的。
现给你一个正整数N,请你找到比N大的最小的那个回文数P。
输入
输入包含多组测试数据。
每组输入一个正整数N,N不超过10000位,并且N不包含前导0。
输出
对于每组输入,输出比N大的最小的那个回文数P。
样例输入 Copy
44
3
175
样例输出 Copy
55
4
181
首先看到这道题,注意到N是一个不超过10000位的整数,我的天哪,my god!!如此之大的数,显然计算机中是没有整型的数据类型能储存这么大的数的,
那要考虑把这个小于10000位的数储存起来,那显然就是把这个数的每一位作为字符储存起来,也就是将此数作为字符串储存起来。
char a[10001];
scanf("%s",&a);
注意到字符串的结尾一定是一以‘\0’(’\0’的ASCII码值为零)截止的,所以字符串的长度一定要开的大于N的位数。我在此处开的是10001,其实为了一般保险起见,避免数组越界等问题的出现,数组最好开的更大一些。(字符串其实就是字符数组)。
接下来的关键就是判断回文串了,什么是回文串呢,也就是把一个数一劈两半,从中间往前读与从中间往后读是一模一样的。例如1234554321这个数,从中间劈开,前半部分是12345,后半部分是54321,前半部分从中间往前读即是54321,与后半部分是相等的,所以此数即是回文串。
所以确定回文串即只用确定前半部分即可,只要前半部分确定,此回文串即确定。
既然涉及到一个数一劈两半的过程,那么就要考虑到这个数的位数是奇还是偶,对于偶数位的数就像上面那个例子一样,而对于奇数位的数则将最中间那个数不参与比较即可。而实际上,这两种情况可以统一表示起来。
题目任务是找比N大的最小一位回文数,而想要它最小,改的数字越靠后越好,注意到这个要找的回文数不会小于前半部分+前半部分倒过来,例如16754382,比他大的最小回文数不会小于“1675”+“5761”,而5761>4382,则此数的最小回文数就是16755761,。
而对于另一种情况16325678,前半部分倒过来2361<5678 ,这个时候前半部分+前半部分倒过来是“1632”+“2361”,这个时候小于原本的数16325678。这时候如果把前半部分改成8765(后半部分倒过来),显然是不对的,对于16325678这样处理是87655678,是十分荒唐的,读者可以举例尝试就可知了。记住我们的原则是改尽可能靠后的位的数。
这个时候该如何处理呢,把前半部分的最后一位进行加1处理,前半部分变成1633,这个时候再把前半部分+前半部分倒过来是“1633”+“3361”,即是最小的回文串16333361.
所以解决这道题的第一步即是判断前半部分倒过来与后半部分比大小,不同的比较结果采用不同的处理方法。
#include<stdio.h>
#include<string.h>
int main()
{
char a[10001];
while(scanf("%s",&a)!=EOF)
{
int len,flag=1;
len=strlen(a);
for(int i=len/2-1;i>=0;i--)
{
if(a[i]>a[len-i-1])
{
flag=0;break;
}
else if(a[i]<a[len-i-1])
{
flag=1;break;
}
}
if(flag)
{
for(int i=(len-1)/2;i>=0;i--)
{
a[i]++;
if(a[i]>'9') a[i]='0';
else break;
}
if(a[0]=='0')
{
a[0]='1';
len++;
a[len/2]='0';
}
}
for(int i=0;i<=(len-1)/2;i++) printf("%c",a[i]);
for(int i=len/2-1;i>=0;i--) printf("%c",a[i]);
printf("\n");
}
return 0;
}
对了,提醒一句,要考虑到两种特殊情况,1.找的是比N大的回文数,那么N=44时,最小的回文串为55 ,即是采用前半部分最后一位加一后再进行操作。 2.进行加一处理,那么9+1会变成0,往前再进一位,如果对于9999,那前半部分99会变成00,这个时候要把首位改成1,即变成10,长度加一,中间的一位变成0,即此回文串即为“10”+“0”+“01”。
最后的任务是将此回文串打印出来,回文串的前半部分已经确定,打印出前半部分+前半部分倒过来即是此回文串。
欧耶,终于写完喽!!!!😎😎😎
二 大数的解决实例之大数取余😎
(撰稿人Someday WWQ)
@[关于小帅的一个数学题]
#题目叙述如下:
既然是数学题,那么题面就要短。hx073269给你一个很大的数N,求它对P取模的结果。
输入
输入包含多组测试用例。
每组测试用例包含一个正整数N(0<N<10^10000) 和一个模数P(0<P<2^32)
输出
对于每组测试用例,输出N%P
样例输入 Copy
10 30
30 10
100 33
样例输出 Copy
10
0
1
#我们知道无论是整型还是浮点型变量都无法存储像N这么多位数的值。
int (16位) -32768~32767
long long或__int64(64位)
-9223372036854775808~9223372036854775807
float(32位) 精确到小数点后6~7位
double (64位) 精确到小数点后15~16位
因此要进行大数处理,利用字符串存储N。P可以用long int或long long 型变量存储
char a[10001]={0};
long long int P;
scanf("%s %ld",a,&x);
之后我们利用循环一位一位地计算,但是这里要注意的是必须计算一次就取模一次。
long long int n=0;
m=strlen(a);
for(i=0;i<m;i++){
n=n*10+(a[i]-'0');
n=n%x;
最后,因为是多样例,所以每次我们都要将数组中的元素清除。
memset(a,0,m);
最终代码如下:
#include<stdio.h>
#include<string.h>
char a[10001]={0};
int main()
{
long long int m,n,i,x;
while(scanf("%s %ld",a,&x)!=EOF){
n=0;
m=strlen(a);
for(i=0;i<m;i++){
n=n*10+(a[i]-'0');
n=n%x;
}
printf("%ld\n",n);
memset(a,0,m);
}
return 0;
}
总结:大数问题一般都需要用字符串处理,大数相加,相减,阶乘,相除等等都是非常经典的问题,对于新手来说很值得多多练习。
这里转发一个计算大数阶乘的例子
三 魔方数问题😎
(撰稿人 sunkcost ZS)
编写程序打印n*n的魔方矩阵,1,2,…n的方阵排列,且每行,每列和每条对角线上的和都相等,由用户指定n的值
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
#include<stdio.h>
int a[100][100];
int main()
{
int row,line,i;
int n;
scanf("%d",&n);
row=0;
line=n/2;//首先奇数的打印
if(n%2!=0){
for(i=1;i<=n*n;i++)
{
if(a[row][line]!=0)
{
row=(row+2)%n;
line=(line-1+n)%n;
}
a[row][line]=i;
row=(row-1+n)%n;
line=(line+1)%n;
}
}
else { //偶数的打印
int i=1;
for(row=0;row<n;row++){
for(line=0;line<n;line++){
if((row+1)%4!=(line+1)%4&&((row+1)%4+(line+1)%4)!=1){
a[row][line]=i;
}
i++;
}
}
i--;
for(row=0;row<n;row++){
for(line=0;line<n;line++){
if((row+1)%4==(line+1)%4||((row+1)%4+(line+1)%4)==1){
a[row][line]=i;
}
i--;
}
}
}
for(int k=0;k<n;k++){
for(int j=0;j<n;j++){
printf("%4d ",a[k][j]);
}
printf("\n");
}
return 0;
}
魔方数有个重要的点就是要分奇数与偶数,奇数相对来说比较容易,只需要找准1的位置,依次找规律遍历所有n的平方以内的数,还是比较容易的。
那么关键来了,偶数怎么处理?偶数对需要两次大的循环,实际上还是判断条件对每个数进行赋值,例如我这里就是利用每次行与列的关系进行判断来调整每次的值。
总结一下奥,还是每次对n平方以内数的安排顺序,所以还是不懂?😂
再说简单一点就是奇数每次上下遍历,偶数是行列每次对4的关系进行赋值。
本次小编就总结到这了,希望大家喜欢哈!记得一键关注,下期分析敬请期待😘