C语言程序设计100例之(61):数字对
例61 数字对
问题描述
对于一个数字对(a, b),我们可以通过一次操作将其变为新数字对(a+b, b)或(a, a+b)。
给定一正整数n,问最少需要多少次操作可将数字对(1, 1)变为一个数字对,该数字对至少有一个数字为n。
输入
第一行一个正整数 n(1 <= n <= 106)。
输出
一个整数表示答案。
输入样例
5
输出样例
3
样例解释
(1,1) → (1,2) → (3,2) → (5,2)
(1)编程思路。
设函数int calc(int a,int b)的返回值是将数字对 (a,b)(不妨设a>b)还原为(1,1)所需的最少操作次数。
显然,若b==1,则calc(a,b)的值为a-1;若b==0,则不可能还原为(1,1),其返回值设定为INF(一个尽可能大的正整数)。
为了加速(a,b)还原为(1,1),不用每次从a中减去1个b,可以采用类似辗转相除法的思想,用a/b次操作将其还原为(b,a%b)。
按上面的思路,将函数int calc(int a,int b)写成递归函数。
求出函数值calc(n,1)、calc(n,2)、calc(n,3)、…、calc(n,n-1)中的最小值,即是求得的最少操作次数。
(2)源程序。
#include <stdio.h>
#define INF 0x3fffffff
int calc(int a,int b)
{
if (b==1) return a-1;
if (!b) return INF;
return a/b+calc(b,a%b);
}
int main()
{
int n,ans;
scanf("%d",&n);
ans=n-1;
int i;
for (i=1;i<n;i++)
{
int t=calc(n,i);
if (t<ans) ans=t;
}
printf("%d",ans);
return 0;
}
习题61
61-1 分解因数
问题描述
给出一个正整数a,要求分解成若干个正整数的乘积,即a = a1 * a2 * a3 * ... * an,并且1 < a1 <= a2 <= a3 <= ... <= an,问这样的分解的种数有多少。注意到a = a也是一种分解。
输入
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a (1 < a < 32768)。
输出
n行,每行输出对应一个输入。输出应是一个正整数,指明满足要求的分解的种数。
输入样例
2
2
20
输出样例
1
4
(1)编程思路。
设函数fun(int a,int n)表示从a开始对n进行因式分解。显然,可以用循环
for (i=a;i<=sqrt(n);i++) 来分解n的每一个约数,
若n%i==0,表示i是n的一个约数,i*(n/i)就是一个满足要求的分解式,计数count++,之后继续对n/i进行分解,即可以递归调用fun(i,n/i)。
(2)源程序。
#include<stdio.h>
int count;
void fun(int a,int n)
{
int i=n,j;
for(i=a;i<n;i++)
{
if(n%i==0 && i<=n/i)
{
count++;
fun(i,n/i);
}
if(i>n/i) break;
}
}
int main()
{
int n,m,i;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&m);
count=1; // m=m也是一种分解
fun(2,m);
printf("%d\n",count);
}
return 0;
}
61-2 波兰表达式
问题描述
波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的波兰表示法为+ 2 3。波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的波兰表示法为* + 2 3 4。本题求解波兰表达式的值,其中运算符包括+ - * /四个。
输入
输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数。
输出
输出为一行,表达式的值。
输入样例
* + 11.0 12.0 + 24.0 35.0
输出样例
1357.000000
(1)编程思路。
根据波兰表达式的定义进行递归求解。在递归函数中,针对当前的输入,有五种情况:
1)输入是运算数,则表达式的值就是这个运算数;
2)输入是’+’,则表达式的值是再继续读入两个表达式并计算出它们的值,然后将它们的值相加;
3)输入是’-’,则表达式的值是再继续读入两个表达式并计算出它们的值,然后将它们的值相减;
4)输入是’*’,则表达式的值是再继续读入两个表达式并计算出它们的值,然后将它们的值相乘;
5)输入是’/’,则表达式的值是再继续读入两个表达式并计算出它们的值,然后将它们的值相除。
(2)源程序。
#include <stdio.h>
#include <stdlib.h>
double fun(void)
{
char a[10];
double t;
scanf("%s", a);
switch(a[0])
{
case'+': return fun() + fun();
case'-': return fun() - fun();
case'*': return fun() * fun();
case'/': return fun() / fun();
default: t=atof(a);
return t;
}
}
int main()
{
double ans;
ans = fun();
printf("%f\n", ans);
return 0;
}
61-3 四舍五入
问题描述
对于给定的数字,如果大于10,将其四舍五入到最接近的10,然后(如果结果大于100)将结果四舍五入到最接近的100,然后(如果结果大于1000)将该数字四舍五入到最接近的1000,…,依此类推。
输入
输入第1行包含一个整数n,表示要舍入的整数个数。
接下来的n行各包含一个整数x(0<=x<=9999999)。
输出
对于输入中的每个整数,在一行中显示取整的整数。
输入样例
9
15
14
4
5
99
12345678
44444445
1445
446
输出样例
20
10
4
5
100
10000000
50000000
2000
500
(1)编程思路。
设函数int rounders(int a,int b) 功能是将整数a四舍五入到最接近的b。可以编写成简单的递归函数。参见源程序。
为了方便对这个递归函数的理解,下面以样例中1445的计算为例进行说明。
1445的四舍五入从个位开始,调用函数rounders(1445,10),因为1445%10>=10/2,所以个位的5向前进位,即(1445/10+1)*10=1450;再对1450的十位进行四舍五入,递归调用rounders(1450,100),因为1450%100>=100/2,所以十位的5向前进位,即(1450/100+1)*100=1500;再对1500的百位进行四舍五入,递归调用rounders(1500,1000),因为1500%1000>=1000/2,所以百位的5向前进位,即(1500/1000+1)*1000=2000;再对2000的千位进行处理,递归调用rounders(2000,10000),因为2000<10000,递归调用结束,最终返回结果为2000。
再以样例中12345678的计算为例进行说明。
12345678的四舍五入从个位开始,调用函数rounders(12345678,10),因为12345678%10=8>=10/2,所以个位的8向前进位,即(12345678/10+1)*10=12345680;再对12345680的十位进行四舍五入,递归调用rounders(12345680,100),因为12345680%100=80>=100/2,所以十位的8向前进位,即(12345680/100+1)*100=12345700;再对12345700的百位进行四舍五入,递归调用rounders(12345700,1000),因为12345700%1000=700>=1000/2,所以百位的7向前进位,即(12345700/1000+1)*1000=12346000;再对12346000的千位进行处理,递归调用rounders(12346000,10000),因为12346000%10000=6000>=10000/2,所以千位的6向前进位,即(12346000/10000+1)*10000=12350000;再对12350000的万位进行处理,递归调用rounders(12350000,100000),因为12350000%100000=50000>=100000/2,所以万位的5向前进位,即(12350000/100000+1)*100000=12400000;再对12400000的十万位进行处理,递归调用rounders(12400000,1000000),因为12400000%1000000=400000<1000000/2,所以十万位的4舍去不向前进位,即(12400000/1000000+1)*1000000=12000000;再对12000000的百万位进行处理,递归调用rounders(12000000,10000000),因为12000000%10000000=2000000<10000000/2,所以百万位的2舍去不向前进位,即(12000000/10000000+1)*10000000=10000000;再对10000000的千万位进行处理,递归调用rounders(10000000,100000000),因为10000000<100000000,递归调用结束,最终返回结果为10000000。
(2)源程序。
#include<stdio.h>
int rounders(int a,int b)
{
if (a<=b) return a;
if (a%b<b/2) return rounders((a/b)*b,b*10); // 四舍
return rounders((a/b+1)*b,b*10); // 五入
}
int main()
{
int n;
scanf("%d",&n);
while (n--)
{
int x;
scanf("%d",&x);
printf("%d\n",rounders(x,10));
}
return 0;
}