C语言—NYOJ上的几道水题
上周做完了NYOJ上的语言入门80题,虽然都是难度比较低的水题,但是有些较好的题还是想拿出来和大家分享一下,其中并不涉及数据结构和算法,希望能对大家有帮助。
1. 素数距离问题
描述
现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度。如果左右有等距离长度素数,则输出左侧的值及相应距离。
如果输入的整数本身就是素数,则输出该素数本身,距离输出0
输入
第一行给出测试数据组数N(0 < N <= 10000)
接下来的N行每行有一个整数M(0 < M < 1000000),
输出
每行输出两个整数 A B.
其中A表示离相应测试数据最近的素数,B表示其间的距离。
- 解题思路:首先判断输入的这个数是不是素数(特殊情况:当这个数是1时,离他最近的素数就是2,距离为1),如果是素数,则输出他本身,距离为1。如果这个数不是素数,在他的左边和右边分别找到距离最近的素数,并且比较哪个更近,若距离相等则输出左边的值和距离。
注意点:
- 1. 分清break和continue的区别
- 2.判断素数的方法,注意特殊值
代码:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int main(){
int n,x;
int i,j;
scanf("%d",&n);
while(n--){
int dis1,dis2;
int x1,x2,k;
scanf("%d",&x);
for(i=2;i<=sqrt(x);i++)
if(x%i==0)
break;
if(x==1){
printf("2 1\n");
continue;
}
if(i>sqrt(x)){
printf("%d 0\n",x);
continue;
}
for(i=x-1;i>=2;i--){
for(j=2;j<=sqrt(i);j++)
if(i%j==0)
break;
if(j>sqrt(i)){
x1=i;
break;
}
}
for(i=x+1;;i++){
for(j=2;j<=sqrt(i);j++)
if(i%j==0)
break;
if(j>sqrt(i)){
x2=i;
break;
}
}
k=((x-x1)<=(x2-x))?x1:x2;
printf("%d %.0f\n",k,fabs(x-k));
}
return 0;
}
2. 蛇形填数
描述
在n*n方陈里填入1,2,…,n*n,要求填成蛇形。例如n=4时方陈为:
10 11 12 1
9 16 13 2
8 15 14 3
7 6 5 4
输入
直接输入方陈的维数,即n的值。(n<=100)
输出
输出结果是蛇形方陈。
- 解题思路:输入n为矩阵的阶数,定义一个整型二维数组,让一个变量每次自增1就可以,但是该怎么给数组元素赋值呢?内层循环:沿四个方向赋值并自增。外层循环:每次降两阶,继续上述操作即可。
注意点:
- 1. 循环的判断条件
代码:
#include<stdio.h>
int main(){
int n,i,j,k;
int a[101][101];
int index=1;
scanf("%d",&n);
for(i=1,k=0;i<n;i++,k++){
for(j=k;j<n-k&&index<=n*n;j++)
a[j][n-k-1]=index++;
for(j=n-k-2;j>=k&&index<=n*n;j--)
a[n-k-1][j]=index++;
for(j=n-k-2;j>=k&&index<=n*n;j--)
a[j][k]=index++;
for(j=k+1;j<n-k-1&&index<=n*n;j++)
a[k][j]=index++;
}
for(i=0;i<n;i++){
for(j=0;j<n;j++)
printf("%d ",a[i][j]);
printf("\n");
}
}
3. 6174问题
描述
假设你有一个各位数字互不相同的四位数,把所有的数字从大到小排序后得到a,从小到大后得到b,然后用a-b替换原来这个数,并且继续操作。例如,从1234出发,依次可以得到4321-1234=3087、8730-378=8352、8532-2358=6174,又回到了它自己!现在要你写一个程序来判断一个四位数经过多少次这样的操作能出现循环,并且求出操作的次数
比如输入1234执行顺序是1234->3087->8352->6174->6174,输出是4
输入
第一行输入n,代表有n组测试数据。
接下来n行每行都写一个各位数字互不相同的四位数
输出
经过多少次上面描述的操作才能出现循环
- 解题思路:输入这个数,将这个数的每一位存储在一个整形数组中,然后进行排序,用所组成的最大四位数减去最小四位数,判断是否是以前的数,如果是,则输出循环次数,否则继续循环。
注意点:
- 1. 要用一个变量来保存上一次循环后得到的值
代码:
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
while(n--){
int a[4],x,num,i,sum=0;
int j,k;
int q=0;
scanf("%d",&x);
num=x;
while(1){
if(num!=x)
num=q;
i=3;
while(num){
a[i]=num%10;
num/=10;
i--;
}
for(j=0;j<3;j++)
for(k=0;k<3-j;k++)
if(a[k]<a[k+1]){
int temp=a[k];
a[k]=a[k+1];
a[k+1]=temp;
}
num=a[0]*1000+a[1]*100+a[2]*10+a[3]-a[3]*1000-a[2]*100-a[1]*10-a[0];
sum++;
if(num==q){
printf("%d\n",sum);
break;
}
q=num;
}
}
return 0;
}
4. 关于521
描述
Acm队的流年对数学的研究不是很透彻,但是固执的他还是想一头扎进去。
浏览网页的流年忽然看到了网上有人用玫瑰花瓣拼成了521三个数字,顿时觉得好浪漫,因为每个男生都会不经意的成为浪漫的制造者。此后,流年走到哪里都能看到5、2、1三个数字,他怒了,现在他想知道在连续的数中有多少数全部包含了这三个数字。例如12356就算一个,而5111就不算。特别的,如果他看到了521三个数连续出现,会特别的愤怒。例如35210。
输入
多组测试数据:
一行给定两个数a,b(0< a,b < 1000000),表示数字的开始和结束。
输出
一行显示他想要知道的数有几个及显示有多少个数字令他特别的愤怒。用空格隔开。
- 解题思路:状态分为 普通愤怒 和 特别愤怒 两种 ,从100到1000000算出每个值之前所积累的普通愤怒和特别愤次数怒,最后再从所给区间末尾减去开头,就求出了中间的次数。
注意点:
- 1. 很麻烦,自己看代码吧
代码:
#include<stdio.h>
int c[1000000]={0},f[1000000]={0};
int main(){
int b[100]={0};
int a,d,i,j,Q,W,E,m,w,x,t=1,s,F,X;
for(i=100;i<1000000;i++){
s=i;w=0;
c[i]=c[i-1];
f[i]=f[i-1];
while(s){
b[w]=s%10;
s=s/10;
w++;
}
Q=0;W=0;E=0;F=0;
for(s=0;s<w;s++){
if(b[s]==5)
Q++;
if(b[s]==2)
W++;
if(b[s]==1)
E++;
if(b[s]==1&&b[s+1]==2&&b[s+2]==5){
F++;
}
}
if(Q!=0&&W!=0&&E!=0){
c[i]++;
}
if(F!=0){
f[i]++;
}
}
while(scanf("%d %d",&a,&d)!=EOF){
printf("Case %d:%d %d\n",t++,c[d]-c[a-1],f[d]-f[a-1]);
}
return 0;
}
5. 数字分隔
描述
在一个遥远的国家,银行为了更快更好的处理用户的订单,决定将一整串的数字按照一定的规则分隔开来,分隔规则如下:
1、实数的整数部分按照每三个数字用逗号分隔开(整数部分的高位有多余的0时,需先将多余的0过滤后,再进行数字分隔,如:0001234567 输出结果为1,234,567.00)
2、小数部分保留两位小数(四舍五入)
3、如果该数是负的,则在输出时需用括号将分隔后的数字括起来,例如:-10005.1645的输出结果为(10,005.16)
输入
多组测试数据(以eof结尾),每行输入一个实数n(n的位数小于100)
输出
输出分隔后的结果
- 解题思路: 代码写了注释,自己看吧,我脑壳疼
代码:
#include <stdio.h>
#include <string.h>
#define maxn 110
char buf[110];
int main(){
int sign,left,dot,i,len;
char *ch;
while(scanf("%s", buf + 1) != EOF){ //buf字符数组从下标为一的元素开始录入
sign = 0;
buf[0] = '0'; //给字符数组的 下标为零的元素赋'0'
if(buf[1] == '-') { //判断正负
sign = 1;
buf[1] = '0';
}
len = strlen(buf); //len表示整个数据的长度
if(ch = strchr(buf, '.')) { //strchr()用来查找在字符串中 字符第一次出现的位置 失败返回NULL
dot = ch - buf; //对指针进行算数运算,dot代表不含小数部分的长度
}
else dot = len;
if(len - dot > 3) { //小数部分 大于两位时情况
if(buf[dot+3] > '4') { //第三位小数大于等于5时
if(++buf[dot+2] > '9'){ //第二位小数可以进位时
buf[dot+2] = '0'; //第二位归零
if(++buf[dot+1] > '9') { //当第一位小数可以进位时
buf[dot+1] = '0'; //第一位归零
++buf[dot-1]; //给个位加一
}
}
i = dot - 1; // 给i 赋个位的位置
while(buf[i] > '9'){ //整数部分的进位
buf[i--] = '0';
++buf[i];
}
}
buf[dot+3] = '\0'; //小数两位后结束字符串
}
else if(len - dot == 2){ //当正好有一位小数时
buf[len++] = '0'; //小数后补'0'
buf[len] = '\0'; //结束字符串
}
else if(len - dot == 1) { //没有小树时
buf[len++] = '0';
buf[len++] = '0';
buf[len] = '\0';
}
else if(len - dot == 0){ //没有点时
buf[len++] = '.'; //加点,补两位0
buf[len++] = '0';
buf[len++] = '0';
buf[len] = '\0';
}
for(left = 0; buf[left] == '0'; ++left); //跳过字符数组前面的'0'
if(buf[left] == '.') --left; //若第一位就是'.'
if(sign) putchar('('); // 负数时输出(
while(left < dot) { //依次输出 整数部分
putchar(buf[left++]);
if(left != dot && (dot - left) % 3 == 0) //当下一位不是'.',并且点到当前位置的差能被3整除时,输出','
putchar(',');
}
printf("%s", buf + dot); //输出小数点和小数部分
if(sign) putchar(')');
putchar('\n');
}
return 0;
}