学习目标:
学会用常量表简化代码
学会用状态变量辅助字符串读入
学会用结构体定义高精度证书,并设计构造函数、复制构造函数和输入输出方法
学会为结构体定义“小于”运算符,并用它定义其他比较运算符
熟悉掌握冒泡排序和排序检索
熟练掌握用qsort库函数给整数和字符串排序的方法
熟练掌握小规模素数表的构造方法
熟练掌握素因子分解的方法
熟练掌握三角形有向面积的意义和计算方法
完成一定数量的编程练习
5.1.1 WERTYU
把手放在键盘上时,稍不注意就会往右错一位。这样的话,Q会变成W,J会变成K等。输入一个错位后敲出的字符串,输出打字员本来想打出的句子。
样例输入:Q S, GOMR YPFSU/
样例输出: I AM FINE TODAY.
代码:
#include<stdio.h> #include<string.h> char *s="`1234567890-=QWERTYUIOP[]\ASDFGHJKL;'ZXCVBNM,./";//定义一个常数数组 int main() { int i; char c; while((c=getchar())!=EOF) { for(i=1;s[i]&&s[i]!=c;i++);//此处的;千万不可以省去 if(s[i]) putchar(s[i-1]); else putchar(c); } return 0; }
5.1.2 TeX 括号
在TeX中,左双引号是"。输入一篇包含双引号的文章,你的任务是把它转换成TeX的格式。
样例输入:
"To be or not to be,"quoth the Bard,"that is the question".
样例输出:
“To be or not to be,"quoth the Bard,“that is the question".
代码:
#include<stdio.h> int main() { int c,q=1; while((c=getchar())!=EOF) { if(c=='"') { printf("%s",q?"“":"''" ); q=!q; } else printf("%c",c); } return 0; }
5.1.3 周期串
如果一个字符串可以有某个长度为k的字符串重复多次得到,我们说该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。输入一个长度不超过80的串,输出它的最小周期。
样例输入:HoHoHo
样例输出:2
代码:
#include<stdio.h> #include<string.h> int main() { int i,k,j; char word[100]; scanf("%s",word); int len=strlen(word); for(i=1;i<=len;i++) if(len%i==0) { k=1; for(j=i;j<len;j++) if(word[j]!=word[j%i]) { k=0; break; } if(k!=0) { printf("%d ",i); break; } } return 0; }
5.2.1 小学生算术
很多学生在学习加法时,发现“进位”特别容易出错,你的任务是计算两个整数在相加时需要多少次进位。你编制的程序应当可以连续处理多组数据,知道读到两个0(这是输入结束标记)。假设输入的整数都不超过9个数字。
样例输入:
123 456
555 555
123 594
0 0
样例输出:
0
3
1
代码:
#include<stdio.h> int main() { int a,b; while(~scanf("%d %d",&a,&b)) { if(a==0&&b==0) break; int c=0,ans=0; for(int i=9;i>=0;i--) { c=(a%10+b%10+c)>9?1:0; ans+=c; a/=10; b/=10; } printf("%d ",ans); } return 0; }
5.2.2 阶乘的精确值
输入不超过1000的正整数n,输出n!=1×2×3×……×n的精确结果。
样例输入: 30
样例输出: 265252859812191058636308480000000
代码:
#include<stdio.h> #include<string.h> const int maxn=3000;//const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数, //所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。 int f[maxn]; int main() { int i,j,n; scanf("%d",&n); memset(f,0,sizeof(f)); f[0]=1; for(int i=2;i<=n;i++) { int c=0; for(j=0;j<maxn;j++) { int s=f[j]*i+c; f[j]=s%10; c=s/10; } } for(j=maxn-1;j>=0;j--) if(f[j]) break; for(i=j;i>=0;i--) printf("%d",f[i]); printf(" "); return 0; }
!!!!高精度运算类bign
代码:
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> #include <string> #include <vector> #include <list> using namespace std; const int maxn = 1000; struct bign { int len, s[maxn]; //s是逆序存储 bign(){ memset(s, 0 , sizeof(s)); len = 1 ; } //构造函数 bign(int num) { *this = num ; } //初始化 bign(const char* num) { *this = num ; } string str() const//转换为字符串 { int i; string res = ""; for( i = 0 ; i < len ; i++ ) res = (char)( s[i] + '0' ) + res ; if (res == "") res = "0"; return res; } bign operator = (const char* num)//赋值运算1 { int i; len = strlen(num); for( i = 0 ; i < len ; i++ ) s[i] = num[len-i-1] - '0' ; return *this; } bign operator = (int num)//赋值运算2 { char s[maxn]; sprintf(s , "%d" , num); *this = s; return *this; } bign operator + (const bign& b) const//加法 { bign c; c.len = 0; int i, g; for( i = 0 , g = 0 ; g || i < ( len > b.len ? len : b.len ) ; i++ ) { int x = g ; if( i < len ) x += s[i] ; if( i < b.len ) x += b.s[i] ; c.s[c.len++] = x % 10 ; g = x / 10 ; } return c; } //比较的前提是两个数都没有前导零 bool operator < (const bign& b) const { if( len != b.len ) return len < b.len ; int i; for( i = len-1 ; i >= 0 ; i-- ) if( s[i] != b.s[i] ) return s[i] < b.s[i]; return false; } bool operator > (const bign& b) const { return b < *this; } bool operator <= (const bign& b) const { return !(b < *this); } bool operator >= (const bign& b) const { return !(*this < b); } bool operator != (const bign& b) const { return b < *this || *this < b; } bool operator == (const bign& b) const { return !(b < *this) || !(*this < b); } }; istream& operator >> (istream &in , bign& x)//定义<<运算符 { string s; in >> s; x = s.c_str(); return in; } ostream& operator << (ostream &out , const bign& x)//定义>>运算符 { out << x.str(); return out; }
5.3.1 6174问题
假设你有一个各种数字互不相同的四位数,把所有数字从大到小排序后得到a,从小到大排序得到b,然后用a-b替换原来这个数,并且继续操作。例如,从1234出发,依次可以得到4321-1234=3087、8730-378=8352、8532-2358=6174、7641-1467=6174,回到了它自己。
输入一个n位数,输出操作序列,直到出现循环(即新得到的数曾经得到过)。输入保证在循环之前最多只会产生1000个整数。
样例输入:1234
样例输出:1234->3087->8352->6174->6174
代码:
#include<stdio.h> #include<string.h> int get_next(int x) { int a,b,n; char s[10]; sprintf(s,"%d",x);//字符串格式化命令,主要功能是把格式化的数据写入某个字符串中。sprintf 是个变参函数。 n=strlen(s); for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) if(s[i]>s[j]) { char t=s[i]; s[i]=s[j]; s[j]=t; } sscanf(s,"%d",&b);//sscanf() - 从一个字符串中读进与指定格式相符的数据. for(int i=0;i<n/2;i++) { char t=s[i]; s[i]=s[n-1-i]; s[n-1-i]=t; } sscanf(s,"%d",&a); return a-b; } int num[2000],count; int main() { scanf("%d",&num[0]); printf("%d",num[0]); count=1; for(;;) { num[count]=get_next(num[count-1]); printf("->%d",num[count]); int found=0; for(int i=0;i<count;i++) if(num[i]==num[count]) { found=1; break; } if(found) break; count++; } printf(" "); return 0; }
5.3.2 字符重排
输入一个字典(用******结尾),然后再输入若干单词。每输入一个单词w,你都需要在字典中找出所有可以用w的字母重排后得到的单词,并按照字典序从小到大的顺序在一行中输出(如果不存在,输出:()。输入单词之间用空格或空行隔开,且所有输入单词都由不超过6个小写字母组成。注意,字典中的单词不一定按字典序排序。
样例输入:
tarp given score refund only trap work earn course pepper part
******
resco nfudre aptr sett oresuc
样例输出:
score
refund
part trap tarp
:(
course
代码:
#include<stdio.h> #include<string.h> #include<stdlib.h> int n; char word[2000][10],sorted[2000][10]; //字符比较函数 int cmp_char(const void* _a,const void* _b) { char* a=(char*)_a; char* b=(char*)_b; return *a-*b; } //字符串比较函数 int cmp_string(const void* _a,const void* _b) { char* a=(char*)_a; char* b=(char*)_b; return strcmp(a,b); } int main() { n=0; for(;;) { scanf("%s",word[n]); if(word[n][0]=='*') break;//遇到结束标志就终止循环 n++; } qsort(word,n,sizeof(word[0]),cmp_string);//给所有单词排序 for(int i=0;i<n;i++) { strcpy(sorted[i],word[i]); qsort(sorted[i],strlen(sorted[i]),sizeof(char),cmp_char);//给每个单词排序 } char s[10]; while(scanf("%s",s)==1)//持续读取到文件结尾 { qsort(s,strlen(s),sizeof(char),cmp_char);//给输入单词排序 int found=0; for(int i=0;i<n;i++) if(strcmp(sorted[i],s)==0) { found=1; printf("%s",word[i]);//输出原始单词,而不是排序后的 } if(!found) printf(":("); printf(" "); } return 0; }
5.4.1 Cantor的数表
如下列数,第一项是1/1,第二项是1/2,第三项是2/1,第四项是3/1,第五项是2/2,……。输入n,输出第n项。
1/1 1/2 1/3 1/4 1/5
2/1 2/2 2/3 2/4
3/1 3/2 3/3
4/1 4/2
5/1
样例输入:
3
14
7
12345
样例输出:
2/1
2/4
1/4
59/99
分析:
按照斜线分类,第一条斜线有一个数,第二条斜线有2个数……第i条有i个数。前i条斜线一共有S(k)=1+2+3+……+k=1/2*k*(k+1)个数。
第k条斜线的倒数第i个元素是i/(k+1-i)。
代码:
//利用代数,避免浮点误差 #include<stdio.h> #include<math.h> int main() { int n; while(scanf("%d",&n)==1) { int k=(int)floor((sqrt(8.0*n+1)-1)/2-1e-9)+1; int s=k*(k+1)/2; printf("%d/%d ",s-n+1,k-s+n); } return 0; }
5.4.2 因子和阶乘
输入正整数n(2≤n≤100),把阶乘n!=1×2×3×……×n分解成素因子相乘的形式,从小到大输出各个素数(2、3、5……)的指数。
例如825=3×52×11应表示成(0,1,2,0,1),表示分别有0、1、2、0、1个2、3、5、7、11.你的程序应忽略比最大素因子更大的素数(否则末尾会有无穷多个0)。
样例输入:
5
53
样例输出:
5!= 3 1 1
53! = 49 23 12 8 4 4 3 2 2 1 1 1 1 1 1 1
代码:
#include<stdio.h> #include<string.h> //素数判定,注意,n不能太大 int is_prime(int n) { for(int i=2;i*i<=n;i++) if(n%i==0) return 0; return 1; } //素数表 int prime[100],count=0; int main() { //n和各个素数的指数 int n,p[100]; //构造素数表 for(int i=2;i<=100;i++) if(is_prime(i)) prime[count++]=1; while(scanf("%d",&n)==1) { printf("%d!=",&n); memset(p,0,sizeof(p)); int maxp=0; for(int i=1;i<=n;i++) { //必须把i复制到变量m中,而不要在做除法时直接修改它 int m=i; for(int j=0;j<count;j++) while(m%prime[j]==0)//反复除以prime[j],并累加p[j] { m/=prime[j]; p[j]++; if(j>maxp)//更新更大素因子下标 maxp=j; } } //只循环到最大下标 for(int i=0;i<=maxp;i++) printf("%d",p[i]); printf(" "); } return 0; }
5.4.3果园里的树
果园里的树排列成矩阵。它们的x,y坐标均是1~99的整数。输入若干个三角形,依次统计每一个三角形内部和边界上共有多少棵树
样例输入:
1.5 1.5 1.5 6.8 6.8 1.5
10.7 6.9 8.5 1.5 14.5 1.5
样例输出:
15
17
分析:
此处用到了三角形的有向面积
代码:
#include<stdio.h> #include<string.h> #include<math.h> double area2(double x0,double y0,double x1,double y1,double x2,double y2) { return x0*y1+x2*y0+x1*y2-x2*y1-x0*y2-x1*y0; } int main() { double x0,y0,x1,y1,x2,y2; while(~scanf("%lf %lf %lf %lf %lf %lf",&x0,&y0,&x1,&y1,&x2,&y2)) { printf("%d ",(int)fabs(area2(x0,y0,x1,y1,x2,y2))/2+1); } return 0; }