华丽的递归——将正整数表示为平方数之和
题目:There live square people in a square country.Everything in this country is square also.Thus, the Square Parliament has passed a law about a land.According to the law each citizen of the country has a right to buy land.A land is sold in squares, surely.Moreover, a length of a square side must be a positive integer amount of meters.Buying a square of land with a side a one pays a2 quadrics (a local currency) and gets a square certificate of a landowner.One citizen of the country has decided to invest all of his N quadrics into theland. He can, surely, do it, buying square pieces 1 × 1 meters.At the same time the citizen has requested to minimize an amount of pieceshe buys: "It will be easier for me to pay taxes," — he has said.He has bought the land successfully.
Your task is to find out a number of certificates he has gotten.
Input
The only line contains a positive integer N ≤ 60 000 ,that is a number of quadrics that the citizen has invested.
Output
The only line contains a number of certificates that he has gotten.
Sample
input output
344 3
Memory Limit: 16 MB
出处:1073. Square Country(http://acm.timus.ru/problem.aspx?space=1&num=1073).
这个题目的要求是把一个≤60 000的正整数分解为数目最少的平方数之和。
首先需要考虑数据是否在求解范围,下面的伪代码的含义是不言自明的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{是,否}是否_t;
是否_t 数据是否合理(int N);
int main( void )
{
int N = 0 ;
if(数据是否合理(N)==是)
{
printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
是否_t 数据是否合理(int N)
{
if( N > MAX_N || N <= 0 )
{
return 否;
}
return 是;
}
完成相应的代码非常简单,只要通过查找替换就可以了。之后进行测试。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{SHI,FOU} SF_t;
SF_t sj_sf_hl(int N);
int main( void )
{
int N = 1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
现在至少完成了一半的工作 。
下面开始求解问题。题目要求求出“number of quadrics” 且要求 “minimize an amount of pieces”。显然,number是N的函数,且min_number是N的函数。因而可以得到下面的伪代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{SHI,FOU} SF_t;
SF_t sj_sf_hl(int N);
unsigned 最小平方数个数(int);
int main( void )
{
int N = 1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",最小平方数个数(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
unsigned 最小平方数个数(int n)
{
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
完成代码并测试:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{SHI,FOU} SF_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
int main( void )
{
int N = 1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return 1;//这个是为了测试N为1情况而写的伪答案
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
测试通过。
现在研究 “unsigned zx_pfs_gs(int n) //求最小平方数个数 ”这个函数的写法。
首先,N能用有限个平方数之和(回答问题是否有解比解答问题本身更重要),因为最不济它可以表示成N个1之和。
然而现在的问题是N能表示成最少几个平方数之和?很自然我们会问1个是否可以?如果1个不可以两个是否可以? 如果两个不可以三个是否可以?……
换句话说,我们希望函数从1个平方数开始检查,如果不成功就检查2个,……一旦成功就返回平方数的个数。
这个要求可以用下面这样的结构实现
调用:逐个尝试(n,1U) ; //1表示从1个开始尝试
定义:
unsigned 逐个尝试( int n,unsigned 当前尝试的个数 )
{
if(可以())
return 当前尝试的个数;
return 逐个尝试 (n,当前尝试的个数+1);
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{SHI,FOU} SF_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
unsigned zbcs(int ,unsigned );
int main( void )
{
int N = 1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
unsigned zbcs( int n,unsigned dqsm )
{
if(1)// 可以())
return dqsm;
return zbcs(n,dqsm+1);
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return zbcs(n,1U);//逐个尝试(n,1U) //1表示从1个开始尝试
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
对1进行测试,正确,测试通过。
现在研究n是否可以表示为m个平方数之和的问题。
这个问题的提法显然是
SF_t 可以表示(int n, unsigend m);//回答n是否可以表示成m个平方数之和
先写出框架:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{SHI,FOU} SF_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
unsigned zbcs(int ,unsigned );
SF_t ky_bs(int ,unsigned );
int main( void )
{
int N = 1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
SF_t ky_bs(int n,unsigned m)
{
return SHI ;// for 测试
}
unsigned zbcs(int n,unsigned dqsm)
{
if( ky_bs( n, dqsm ) == SHI )// 可以())
return dqsm;
return zbcs(n,dqsm+1);
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return zbcs(n,1U);//逐个尝试(n,1U) //1表示从1个开始尝试
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
现在研究 SF_t ky_bs(int n,unsigned m) 的写法。
函数调用ky_bs( n, m)要回答的问题是n是否可以表示为m个平方数之和,显然这个问题明显等价于:
ky_bs( n - 一个平方数 , m -1 ),亦即减去一个平方数之后是否可以表示为m-1个平方数。
最终要面对的问题是:
ky_bs( n - (m-1)个平方数 , 1 ),亦即判断某个数是否就是一个完全平方数 。
下面给出原理性示意代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum{SHI,FOU} SF_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
unsigned zbcs(int ,unsigned );
SF_t ky_bs(int ,unsigned );
int main( void )
{
int N = 3; //2 ;//1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
SF_t ky_bs(int n,unsigned m)
{
if(m==1U)
{
if(n==1)
return SHI ;
else
return FOU ;
}
return ky_bs( n - 1, m - 1U) ;// 现在可以测试 N= 1,2,3
}
unsigned zbcs(int n,unsigned dqsm)
{
if( ky_bs( n, dqsm ) == SHI )// 可以())
return dqsm;
return zbcs(n,dqsm+1);
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return zbcs(n,1U);//逐个尝试(n,1U) //1表示从1个开始尝试
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
好的,对1,2,3的测试通过了。程序成功地算出了1,2,3分别为最少1,2,3个完全平方数之和。
下面考虑解决N==4的问题。
很显然,只要解决了判断一个正整数是否是完全平方数并将之替换ky_bs(int n,unsigned m)函数中的“n==1”就可以解决这个问题。而判断某个数是否是完全平方数是非常容易解决的(参见拙著《狂人C》p148)。
但是出于卖弄一下递归的虚荣心理,所以在这里我首先构造一个小小的数据结构:
typedef struct{ unsigned 平方;unsigned 奇数;} 平方_奇数_t;
这个数据结构能够帮助我们用递归的方法完成判断一个正整数是否是完全平方数。
原理如下:
原型:SF_t 是否完全平方(int ,平方_奇数_t);
定义:
SF_t 是否完全平方(int n,平方_奇数_t pf_js )
{
if(n==pf_js.平方)
return 是;
if(n<pf_js.平方)
return 否;
{
平方_奇数_t 下一个;
下一个.奇数 = pf_js.奇数 + 2 ;
下一个.平方 = pf_js.平方 + 下一个.奇数 ;
return 是否完全平方(n,下一个) ;
}
}
据此完成代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum {
SHI ,
FOU
} SF_t;
typedef struct {
unsigned pf;
unsigned js;
} PF_JS_t; //typedef struct{ unsigend 平方,unsigend 奇数} 平方_奇数_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
unsigned zbcs(int ,unsigned );
SF_t ky_bs(int ,unsigned );
SF_t sf_pf( int ,PF_JS_t ) ;
int main( void )
{
int N = 16;//9;//4;//3; //2 ;//1;//MAX_N ;//MAX_N+1;//-1;//0 ;
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
SF_t sf_pf( int n , PF_JS_t pf_js )
{
if(n==pf_js.pf)
return SHI;
if(n<pf_js.pf)
return FOU;
{
PF_JS_t xyg;
xyg.js = pf_js.js + 2 ;
xyg.pf = pf_js.pf + xyg.js ;
return sf_pf(n,xyg) ;
}
}
SF_t ky_bs(int n,unsigned m)
{
if(m==1U)
{
PF_JS_t qs={1U,1U};
if(sf_pf(n,qs)==SHI)//这里修改为判断n是否是完全平方
return SHI ;
else
return FOU ;
}
return ky_bs( n - 1, m - 1U) ;// 现在可以测试 N= 1,2,3
}
unsigned zbcs(int n,unsigned dqsm)
{
if( ky_bs( n, dqsm ) == SHI )// 可以())
return dqsm;
return zbcs(n,dqsm+1);
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return zbcs(n,1U);//逐个尝试(n,1U) //1表示从1个开始尝试
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
编译通过。如不出意料,现在4,9,16……这样的完全平方数是可以求出是由1个平方数组成。运行,测试——如愿以偿.现在程序已经完成1/4强了。
回头审视代码,ky_bs()函数中的局部变量qs非常别捏。此外发现,SF_t ky_bs(int n,unsigned m)这个函数写得实际上有错误,它还应该需要一个参数。
这个错误非常严重,造成这个错误的原因是当初问题的提法不完整。
现在需要重新写ky_bs()这个函数了。
这个函数的正确提法应该是n能否表示为m个不小于k平方的平方数之和,理由是递归过程中必然会遇到这个问题。例如对于15,首先从1,4,9中选择一个作为尝试,在后面的选择中应该选择不小于前一个选择的平方数作为尝试。这样进行有序选择的理由是问题的解本身是无序的,进行有序选择可以避免重复的解。这样
ky_bs()函数的原型应该是:
SF_t ky_bs(int ,unsigned ,PF_JS_t);
在zbcs()中的第一次调用应该为:
ky_bs(n, dqsm ,(PF_JS_t){1U,1U}) ;
这个函数的定义为:
SF_t ky_bs(int n,unsigned m,PF_JS_t qs)
{
if(n<qs.pf)
{
return FOU;
}
if(m==1U)
{
PF_JS_t qs={1U,1U};
if(sf_pf(n,qs)==SHI)//这里修改为判断n是否是完全平方
return SHI ;
else
return FOU ;
}
if( ky_bs( n - qs.pf , m - 1U, qs)==SHI)
{
return SHI;
}
else
{
return ky_bs( n - qs.pf , m - 1U, xyg(qs) );
}
}
其中xyg(qs)为求下一个qs。
原型:PF_JS_t xyg(PF_JS_t);
定义:
PF_JS_t xyg(PF_JS_t qian)
{
PF_JS_t hou;
hou.js = qian.js + 2 ;
hou.pf = qian.pf + hou.js ;
return hou ;
}
为提高速度,SF_t sf_pf(n,qs)也做了修改。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum {
SHI ,
FOU
} SF_t;
typedef struct {
unsigned pf;
unsigned js;
} PF_JS_t; //typedef struct{ unsigend 平方,unsigend 奇数} 平方_奇数_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
unsigned zbcs(int ,unsigned );
//SF_t ky_bs(int ,unsigned );
SF_t ky_bs(int ,unsigned ,PF_JS_t);
SF_t sf_pf( int ,PF_JS_t ) ;
PF_JS_t xyg(PF_JS_t);
int main( void )
{
int test_N[]={5,6,7,8,10,11,12,13,14,15},t;//for test
int N = 5;//MAX_N ;//MAX_N+1;//-1;//0 ;//16;//9;// 4;//3; //2 ;//1;//
//for test
for(t=0;t<sizeof test_N /sizeof *test_N;t++)
{
N=test_N[t];
printf("%d ",zx_pfs_gs(N));//printf("求解(N)");
}
//for test
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
PF_JS_t xyg(PF_JS_t qian)
{
PF_JS_t hou;
hou.js = qian.js + 2 ;
hou.pf = qian.pf + hou.js ;
return hou ;
}
SF_t sf_pf( int n , PF_JS_t pf_js )
{
if(n==pf_js.pf)
return SHI;
if(n<pf_js.pf)
return FOU;
return sf_pf(n,xyg(pf_js)) ;
}
SF_t ky_bs(int n,unsigned m,PF_JS_t qs)
{
if(n<qs.pf)
{
return FOU;
}
if(m==1U)
{
//PF_JS_t qs={1U,1U};
if(sf_pf(n,qs)==SHI)//这里修改为判断n是否是不小于qs.pf的完全平方
return SHI ;
else
return FOU ;
}
if( ky_bs( n - qs.pf , m - 1U, qs)==SHI)
{
return SHI;
}
else
{
return ky_bs( n - qs.pf , m - 1U , xyg(qs) );//return ky_bs( n - qs.pf , m - 1U, xyg(qs) );
}
}
//SF_t ky_bs(int n,unsigned m)
//{
// if(m==1U)
// {
// PF_JS_t qs={1U,1U};
// if(sf_pf(n,qs)==SHI)//这里修改为判断n是否是完全平方
// return SHI ;
// else
// return FOU ;
// }
// return ky_bs( n - 1, m - 1U) ;// 现在可以测试 N= 1,2,3
//}
unsigned zbcs(int n,unsigned dqsm)//逐步尝试
{
static PF_JS_t const qs={1U,1U};
if( ky_bs( n, dqsm , qs) == SHI )// 可以表示
return dqsm;
return zbcs( n , dqsm + 1 );
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return zbcs(n,1U);//逐个尝试(n,1U) //1表示从1个开始尝试
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
编译之后,重新测试。这次有点心虚,毕竟改动太大了,最主要的是修改了ky_bs()的接口。
不过万幸的是前面的数据都测试通过了。
现在测试5,6,7,8,10,11,12,13,14,15
测试结果为
2 3 4 5 2 3 4 4 3 4
发现有两组数据是错误的
8 13
5 4
由于几乎从来不调试,所以对调试非常生疏,只好用走查的办法排查BUG。
走查发现ky_bs()的逻辑存在错误(递归就是这样,失之毫厘,谬之千里)。
经过一番艰苦卓绝的实在不足与外人道的3个小时左右的折腾(你懂的)……%¥#“:?《%4#@!…:&《“:(*&Z(7Z76”:》*&%……#(此处删去1842个字) ,最后终于:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
typedef enum {
SHI ,
FOU ,
} SF_t;
typedef struct {
unsigned pf;
unsigned js;
} PF_JS_t; //typedef struct{ unsigend 平方,unsigend 奇数} 平方_奇数_t;
SF_t sj_sf_hl(int N);
unsigned zx_pfs_gs(int);
unsigned zbcs(int ,unsigned );
//SF_t ky_bs(int ,unsigned );
SF_t ky_bs(int ,unsigned ,PF_JS_t);
SF_t sf_pf( int ,PF_JS_t ) ;
PF_JS_t xyg(PF_JS_t);
int main( void )
{
int test_N[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20},t;//for test
int N = 16;//9;// 4;//3; //2 ;//1;//5;//MAX_N ;//MAX_N+1;//-1;//0 ;//
//for test
for(t=0;t<sizeof test_N /sizeof *test_N;t++)
{
N=test_N[t];
printf("%d,",zx_pfs_gs(N));//printf("求解(N)");
}
//for test
if(sj_sf_hl(N)==SHI)
{
printf("%d\n",zx_pfs_gs(N));//printf("求解(N)");
}
else
{
puts("超出求解范围");
}
system("PAUSE");
return 0;
}
PF_JS_t xyg(PF_JS_t qian)
{
PF_JS_t hou;
hou.js = qian.js + 2 ;
hou.pf = qian.pf + hou.js ;
return hou ;
}
SF_t sf_pf( int n , PF_JS_t pf_js )
{
if(n==pf_js.pf)
return SHI;
if(n<pf_js.pf)
return FOU;
return sf_pf(n,xyg(pf_js)) ;
}
SF_t ky_bs(int n,unsigned m,PF_JS_t qs)
{
if(n<qs.pf)
{
return FOU;
}
if(m==1U)
{
if(sf_pf(n,qs)==SHI)
return SHI ;
else
return FOU ;
}
if( ky_bs( n - qs.pf , m - 1U, qs)==SHI )
{
return SHI;
}
else
{
return ky_bs( n , m , xyg(qs) );
}
}
unsigned zbcs(int n,unsigned dqsm)//逐步尝试
{
static PF_JS_t const qs={1U,1U};
if( ky_bs( n, dqsm , qs) == SHI )// 可以表示
return dqsm;
return zbcs( n , dqsm + 1 );
}
unsigned zx_pfs_gs(int n) //求最小平方数个数
{
return zbcs(n,1U);//逐个尝试(n,1U) //1表示从1个开始尝试
}
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
继续测试。 测试结果为
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
输出:
1,2,3,1,2,3,4,2,1,2,3,3,2,3,4,1,2,2,3,2,
这回总算对了。现在我终于可以重新整理一下代码了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*
Name: 正整数分解为平方和问题
Copyright:
Author: 键盘农夫
Date: 04-12-11 00:22
Description: 递归,递归,还是递归
*/
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 60000
#define TEST 1
typedef enum { //我习惯的Bool类型
SHI , //是
FOU , //否
} SF_t;
typedef struct { //这个结构描述了前n个奇数的和为n的平方这个事实
unsigned pf; //平方
unsigned js; //奇数
} PF_JS_t; //平方_奇数_t;
SF_t sj_sf_hl ( int ); //判断输入数据是否合理
unsigned zx_pfs_gs( int ,unsigned ); //求构成某数的平方数的最小个数
SF_t ky_bs( int ,unsigned ,PF_JS_t ); //判断某数是否可以由若干个一定范围的平方数组成
SF_t sf_pf( int ,PF_JS_t ) ; //判断某数是否是完全平方
PF_JS_t xyg( PF_JS_t ); //由某个完全平方数求得下一个完全平方数
int main( void )
{
#if TEST
int test_N[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20},t;
for( t = 0 ; t < sizeof test_N /sizeof *test_N ; t++ )
printf("%d:%d ;" , t + 1 , zx_pfs_gs(test_N[t] , 1U ));
putchar('\n');
#else
int N ;
puts("输入要判断的正整数");
scanf("%d",&N);
if( sj_sf_hl(N) == SHI )
printf("%d\n",zx_pfs_gs( N , 1U ) );
else
puts("超出求解范围");
#endif
system("PAUSE");
return 0;
}
/*
PF_JS_t xyg(PF_JS_t qian);
功能:由qian求得下一个完全平方数(及构成这个数的前n个连续奇数中的最大奇数)
例 :xyg((PF_JS_t){1,1}) -> (PF_JS_t){4,3}
xyg((PF_JS_t){4,3}) -> (PF_JS_t){9,5}
*/
PF_JS_t xyg(PF_JS_t qian)
{
PF_JS_t hou;
hou.js = qian.js + 2 ;
hou.pf = qian.pf + hou.js ;
return hou ;
}
/*
SF_t sf_pf( int n , PF_JS_t pf_js );
功能:判断n是否是不小于pf_js.pf的完全平方数
例 :sf_pf( 1,(PF_JS_t){1,1}) -> SHI
sf_pf( 1,(PF_JS_t){4,3}) -> FOU
sf_pf( 5,(PF_JS_t){4,3}) -> FOU
sf_pf( 9,(PF_JS_t){4,3}) -> SHI
*/
SF_t sf_pf( int n , PF_JS_t pf_js )
{
if( n == pf_js.pf )
return SHI;
if( n < pf_js.pf )
return FOU;
return sf_pf( n , xyg(pf_js) ) ;
}
/*
SF_t ky_bs(int n,unsigned m,PF_JS_t qs) ;
功能:判断n是否由m个不小于qs.pf的完全平方数和表示
例 :ky_bs( 1,1,(PF_JS_t){1,1}) -> SHI
ky_bs( 3,3,(PF_JS_t){1,1}) -> SHI
ky_bs( 4,3,(PF_JS_t){1,1}) -> SHI
ky_bs( 5,2,(PF_JS_t){1,1}) -> SHI
ky_bs( 3,2,(PF_JS_t){1,1}) -> FOU
ky_bs( 9,2,(PF_JS_t){4,3}) -> FOU
ky_bs( 8,2,(PF_JS_t){4,3}) -> SHI
*/
SF_t ky_bs( int n , unsigned m , PF_JS_t qs )
{
if( n < qs.pf )
return FOU;
if( m == 1U )
if( sf_pf( n , qs ) == SHI )
return SHI ;
else
return FOU ;
if( ky_bs( n - qs.pf , m - 1U, qs) == SHI )
return SHI;
else
return ky_bs( n , m , xyg( qs ) );
}
/*
unsigned zx_pfs_gs(int n,unsigned dqsm);
功能:从dqsm起逐步尝试n能表示为几个平方数的和,返回最小值
例 :zbcs( 1 , 1U )-> 1U
zbcs( 2 , 1U )-> 2U
zbcs( 3 , 1U )-> 3U
zbcs( 4 , 1U )-> 1U
zbcs( 5 , 1U )-> 2U
*/
unsigned zx_pfs_gs(int n,unsigned dqsm)
{
static PF_JS_t const qs={1U,1U};
if( ky_bs( n, dqsm , qs) == SHI )
return dqsm;
return zx_pfs_gs( n , dqsm + 1 );
}
/*
SF_t sj_sf_hl(int N);
功能:判断输入数据是否合理
*/
SF_t sj_sf_hl(int N)
{
if( N > MAX_N || N <= 0 )
{
return FOU;
}
return SHI;
}
最后删除了一个没有什么意义的函数,其他部分基本没有什么变化。
总结:
这篇代码刻意地没有使用乘除法 ,相当于自缚一手;同时完全没有使用循环语句, 相当于又缚一手。所以如果我说这是我用脚丫子写出的代码,不至于有人反对吧。
当然,这种刻意的自我束缚在真正的开发中是没有必要的。但作为一种练习,这样可以更深刻地理解程序设计语言的某些结构,极大地提高自己C语言的表达能力。
自觉最精彩的部分是ky_bs()函数,这个函数描述了一种双向的递归,再加上其中对sf_pf()这个递归函数的调用,构成了一种华丽的“三维”“立体”递归。核心是用递归实现广度优先搜索。
最后,这种写法并不需要事先知道“任何一个正整数可以写成四个平方数之和”这样高深的数学定理,显然这种写法对于更一般的同类问题也同样适用。例如,可以用这种办法求解把一个正整数表示为若干立方数之和的问题等等。