计算机程序设计实训记录(3)
解决完了例题后,就是10道oj题,挑一些我认为值得研究的题目记录一下吧
8 乘方计算
问题描述 给定一个unsigned long long型数据$x$,计算$x^2, x^3,\cdots$直到$x^5$或溢出为止。
输入 输入数据有若干行。每行上有一个unsigned long long型整数,对应一种情形。
输出 对于每一种情形,依次输出$x, x^2, x^3, x^4, x^5$的值,不输出计算溢出的数值。数据项之间用逗号、空格分隔。
输入样例
1111
11111
111111
输出样例
1111, 1234321, 1371330631, 1523548331041, 1692662195786551
11111, 123454321, 1371700960631, 15240969373571041
111111, 12345654321, 1371737997260631
【提示】在头文件climits(或limits.h)中宏定义了一些名称,用以表示一些数据类型取值的最大值。但不同的编译系统中宏定义的标识符为不尽一致。例如:有的用ULONG_LONG_MAX,而有的用ULLONG_MAX。可以采用如下办法在程序中统一使用ULONG_LONG_MAX。
#include <limits.h>
#if
ndef LONG_LONG_MAX
#define LONG_LONG_MAX LLONG_MAX
#define ULONG_LONG_MAX ULLONG_MAX
#endif
计算本身并不困难,难点在于如何判断溢出,直接比较大小显然是不可能的,因为在比较的时候就已经溢出了。
# include<stdio.h> # include<math.h> int main() { unsigned long long x,m,n; int i; while(scanf("%llu",&x)==1) { for(i=1;i<=5;i++) { m=powl(x,i); n=powl(x,i+1); if(m<powl(x,(i-1))) break; else if(m>n||i==5) printf("%llu\n",m); else printf("%llu, ",m); } } return 0; }
11 兑换钱币
问题描述 对于给定的人民币金额n(分),问有多少种方案将其兑换成1分、2分、5分。
输入 输入数据有若干行。每行上有一个正整数表示以分为单位的人民币金额n,对应一种情形。
输出 对于每一种情形,输出结果、换行。
输入样例
10
100
150
输出样例
10
541
1186
这道题的解决思路不难想,直接使用穷举法是可以解决的。
#include<stdio.h> int main() { int a,b,c,n=1,i=0; double k=0; while(scanf("%d",&n)!=EOF){ i++; for(a=0;a<=n/5;a++) for(b=0;b<=(n-5*a)/2;b++)//减去已经分配给5分的钱数 for(c=0;c<=n-5*a-2*b;c++)//减去已经分给5分和2分的钱数 { if(c+2*b+5*a==n) k++; } printf("%.0lf\n",k); k=0; } return 0; }
但是使用了三重循环来穷举会造成一个问题,就是运行时间过长,导致在学校OJ系统中TLE。
经过我们的观察不难发现,最后一次循环完全是多余的,只要配凑出5分和2分的数目,就可以解决这个问题。
#include<stdio.h> int main() { int a,b,c,n=1,i=0; double k=0; while(scanf("%d",&n)!=EOF){ i++; for(a=0;a<=n/5;a++) for(b=0;b<=(n-5*a)/2;b++)//减去已经分配给5分的钱数 { if(2*b+5*a<=n) k++; } printf("%.0lf\n",k); k=0; } return 0; }
而经过我优秀的同学们的讨论,也有不用穷举的办法。
因为确定了5分的钱数后,就只剩下2分和1分钱币的组合,而2分钱币可以转化为2个1分钱币,就是另一种组合
如假设共16分,在只有1个五分的情况下,就可以拆成
11=2+2+2+2+2+1
11=2+2+2+2+1+1+1
...
显然,11可以拆成5个2加1个1,就有5+1=6种组合
这样,这个问题就变成了在5分钱确定的情况下,求剩下可拆成最多几个2分钱的问题
#include<stdio.h> int main() { int n; while(scanf("%d",&n)!=EOF) { int i,j,k,l=0; for(i=0; i<=n/5; i++) { k=(n-i*5)/2; l+=k+1; } printf("%d\n",l); } return 0; }
13 指示灯控制
问题描述 有m(m<100)盏灯排成一排(从1到m按顺序依次编号)。灯的开关均为点触式的(即点一次开、再点一次则关)。现有n个人(从1到n依次编号)。第一个人(1号)将灯全部关闭。第二个人(2号)将所有2的倍数编号的灯的开关点一下。第三个人(3号)将所有3的倍数编号的灯的开关点一下。依此类推,当n个人完成其操作后,计算灯亮的数目。
输入 输入数据有若干行。每行上有两个非负整数对应一种情形的m和n。
输出 对于每一种情形,输出计算结果、换行。
输入样例
7 7
3 4
6 4
输出样例
5
2
2
非常有趣的题目,我自己的思路比较复杂。
首先,用一个数组来记录灯的开关情况,0为灭灯,1为亮灯,用一个函数来执行第n个人开关灯的过程,再用另一个函数来统计最后亮着的灯的数目。
开关灯的过程
int *light (int *num,int n,int m) { int i; for(i=0;i<m;i++) { if((i+1)%n==0) { if(num[i]) num[i]=0; else { num[i]=1; } } } return num; }
统计亮着的灯
int lcount(int *num,int m) { int i,count=0; for(i=0;i<m;i++) if(num[i]==1) count++; return count; }
最后的完整代码,可以看到主函数里面还用了一个循环
#include <stdio.h> int *light (int *num,int n,int m) { int i; for(i=0;i<m;i++) { if((i+1)%n==0) { if(num[i]) num[i]=0; else { num[i]=1; } } } return num; } int lcount(int *num,int m) { int i,count=0; for(i=0;i<m;i++) if(num[i]==1) count++; return count; } int main() { static int num[100]; int m,n,i,count; while(scanf("%d %d",&m,&n)!=EOF) { for(i=2;i<n+1;i++) light(num,i,m); count=lcount(num,m); printf("%d\n",count); for(i=0;i<100;i++) num[i]=0; } return 0; }
关于oj题就暂且告一段落,下面将会记录排序算法和字符串处理的内容。