蓝桥杯 程序设计_4

一个N位的十进制正整数,如果它的每个位上的数字的N次方的和等于这个数本身,则称其为花朵数。

例如:

N=3时,153就满足条件,因为 1^3 + 5^3 + 3^3 = 153,这样的数字也被称为水仙花数(其中,“^”表示乘方,5^3表示53次方,也就是立方)。

N=4时,1634满足条件,因为 1^4 + 6^4 + 3^4 + 4^4 = 1634

N=5时,92727满足条件。

实际上,对N的每个取值,可能有多个数字满足条件。

程序的任务是:求N=21时,所有满足条件的花朵数。注意:这个整数有21位,它的各个位数字的21次方之和正好等于这个数本身。

如果满足条件的数字不只有一个,请从小到大输出所有符合条件的数字,每个数字占一行。因为这个数字很大,请注意解法时间上的可行性。要求程序在3分钟内运行完毕。

分析:

由于double的有效位数只有16位,所以不能用来求21位花朵数,因此首先需要可计算21位数字加,乘法的大数加法和大数乘法函数,这里使用字符串实现。程序要求3分钟内运行完毕,因此尽量避免使用malloc函数,因为其很费时间。

思路1:遍历21位数,找处符合条件数字。[优点:思路简单,容易实现,不易出错;缺点:效率低(在配置为CPUi5-2430M,操作系统:Windows 7 旗舰版 的机器上遍历所有9位数字耗时3秒,遍历所有10位数字耗时35…依此累推,遍历所有21位数字需35*1011次方)秒!!!)。]

思路2:尽可能多的排除一些数字,使用递归找出乘方和是21位数的所有21位数,并进一           步判断其是否满足要求。[优点:效率高;缺点:不易实现。]

解:

采用思路2程序如下:

	#define __SPEED_PROGRAM
	
	#include <stdio.h>
	#include <time.h>
	#include <string.h>
	#include <malloc.h>
	#include <stdlib.h>
	#include <math.h>
	
	#define NUMW		21	//数字位数
	#define BOOL		bool
	#define TRUE		true
	#define FALSE		false
	
	typedef char		INT8;
	typedef int			INT32;
	typedef double		DOUBLE;
	typedef void		VOID;
	
	INT32   count = 0;	
	INT8	nPow[10][NUMW+1];	//存放0~9的NUMW次方(ASCII)
	INT8	strMin[NUMW+1];		//最小值
	INT8	strMax[NUMW+2];		//最大值		
	clock_t start_addStr, finish_addStr;
	DOUBLE  duration_addStr = 0;
	
	/***********************************************************************************
	* 函数名: addStr																   *
	* 参数:																		       *
	*		str1:   被加数(ASCII),高位在前,低位在后,'\0'结尾。                      *
	*		str2:   加数(ASCII),高位在前,低位在后,'\0'结尾。						   *
	*		strRes: (out)结果(ASCII),高位在前,低位在后,'\0'结尾。				   *
	*       len:    strRes缓冲区长度。												   *
	* 返回值: TRUE表示成功,FALSE表示失败。                                            *
	* 说明:																		       *
	*		str1, str2可为同1指针,strRes空间足够即可。					               *
	***********************************************************************************/
	BOOL addStr(INT8 *str1, INT8 *str2, INT8 *strRes, INT32 len)
	{
		INT32 len1, len2, i, n;
		INT8 *pTem;
	
	#ifdef __SPEED_PROGRAM
		INT8 pRes[NUMW+2];	//存储空间分配到栈上,节约时间
	#else
		INT8 *pRes;		//动态分配(堆上)
	#endif
	
		start_addStr = clock();
	
		if(NULL==str1 || NULL==str2 || NULL==strRes)
		{
			strRes[0] = '\0';
			return FALSE;
		}
	
		len1 = strlen(str1);
		len2 = strlen(str2);
	
		//使str1指向较长的数
		if(len1 < len2)
		{
			pTem = str1;
			str1 = str2;
			str2 = pTem;
	
			n = len1;
			len1 = len2;
			len2 = n;	
		}
	
		//1:数1或数2为空
		if(0 == len1)
		{
			strcpy(strRes, str2);
			return TRUE;
		}	
		if(0 == len2)
		{
			strcpy(strRes, str1);
			return TRUE;
		}
	
	#ifdef __SPEED_PROGRAM
		n = NUMW + 2;
	#else 
		n = len1 + 2;
		pRes = (INT8 *)malloc(n);
	#endif
		
		for(i=0; i<n; i++)
			pRes[i] = 0;
	
		//计算结果
		i = 0;
		len1--;
		len2--;
		while(len2>=0)	//2:计算<=len2部分结果
		{	
			n = str1[len1--]-'0' + str2[len2--]-'0';
			pRes[i] += n ;
			pRes[i+1] += pRes[i]/10;
			pRes[i] = pRes[i]%10;
			pRes[i++] += '0';
		}
		for(; len1>=0; )	//3:计算len1>len2部分结果
		{
			pRes[i] += str1[len1--] - '0';
			pRes[i+1] += pRes[i]/10;
			pRes[i] = pRes[i]%10;
			pRes[i++] += '0';
		}
		if(pRes[i] > 0)	//4:最高位进位?
		{
			pRes[i++] += '0';
		}
	
		if(len < i+1)
			return FALSE;
	
		//反转复制
		len1 = i;
		for(i=0; i<len1; i++)
			strRes[i] = pRes[len1-i-1];
	
		strRes[i] = '\0';
	
	#ifndef __SPEED_PROGRAM 
		free(pRes);
	#endif
	
		finish_addStr = clock();
		duration_addStr += (DOUBLE)(finish_addStr-start_addStr) / CLOCKS_PER_SEC;
	
		return TRUE;
	}
	
	
	/***********************************************************************************
	* 函数名: mulStr																   *
	* 参数:																		       *
	*		str1:   被乘数(ASCII),高位在前,低位在后,'\0'结尾。                      *
	*		str2:   乘数(ASCII),高位在前,低位在后,'\0'结尾。						   *
	*		strRes: (out)结果(ASCII),高位在前,低位在后,'\0'结尾。			       *
	*       len:    strRes缓冲区长度。												   *
	* 返回值: TRUE表示成功,FALSE表示失败。                                            *
	* 说明:																		       *
	*		str1, str2可为同1指针,strRes空间足够即可。					               *
	***********************************************************************************/
	BOOL mulStr(INT8 *str1, INT8 *str2, INT8 *strRes, INT32 len)
	{
		INT32 len1, len2, i, j, n;
		INT8  *pRes, *pTem;
		pRes = (INT8 *)malloc(len);
	
		memset(pRes, 0, len);
	
		len1 = strlen(str1);
		len2 = strlen(str2);
	
		//使str1指向较长的数
		if(len1 < len2)
		{
			pTem = str1;
			str1 = str2;
			str2 = pTem;
	
			n = len1;
			len1 = len2;
			len2 = n;	
		}
	
		for(i=0; i<len2; i++)
			str2[i] -= '0';
		if(str1 != str2)
			for(i=0; i<len1; i++)
				str1[i] -= '0';
	
		//计算
		for(i=0,j=0; i<len2; i++)	//i=0,j=0防止一个串为空导致的错误
		{
			for(; j<len1; j++)
			{
				n = str2[len2-i-1]*str1[len1-j-1];
				pRes[i+j] += n;
				pRes[i+j+1] += pRes[i+j]/10;
				pRes[i+j] = pRes[i+j]%10;
			}
		}
	
	
		n = i+j;
		while(0 == pRes[n] && n>0)	//去除高位0
			n--;
	
		if(len < n+2)
			return FALSE;
	
		//反转复制
		for(i=0; i<=n; i++)
			strRes[i] = pRes[n-i] + '0';
	
		strRes[i] = '\0';
	
		//恢复str1,str2
		for(i=0; i<len2; i++)
			str2[i] += '0';
		if(str1 != str2)
			for(i=0; i<len1; i++)
				str1[i] += '0';
	
		free(pRes);
	
		return TRUE;
	}
	
	
	/***********************************************************************************
	* 函数名: powStr																   *
	* 参数:																		       *
	*		str:   乘方数(ASCII),高位在前,低位在后,'\0'结尾。                       *
	*		n:     乘方值。															   *
	*		strRes: (out)结果(ASCII),高位在前,低位在后,'\0'结尾。				   *
	*       len:    strRes缓冲区长度。												   *
	* 返回值: TRUE表示成功,FALSE表示失败。                                            *
	* 说明:																		       *
	*		strRes为空间足够即可。										               *
	***********************************************************************************/
	BOOL powStr(INT8 *str, INT32 n, INT8 *strRes, INT32 len)
	{
		INT32 i;
		INT8  *pRes;
		pRes = (INT8 *)malloc(len);
	
		memset(strRes, '\0', len);
	
		if(0 == n)
		{
			strRes[0] = '1';
			return TRUE;
		}
	
		strcpy(strRes, str);
	
		for(i=1; i<n; i++)
		{
			if(FALSE == mulStr(strRes, str, pRes, len))
				return FALSE;
	
			strcpy(strRes, pRes);
	
		}
	
		return TRUE;
	}
	
	INT32 nstrcmp(INT8 *str1, INT8 *str2)
	{
		INT32 len1 = strlen(str1);
		INT32 len2 = strlen(str2);
		if(len1 > len2)
			return 1;
		else if(len1 < len2)
			return -1;
		else
			return strcmp(str1, str2);
	}
	
	/***********************************************************************************
	* 函数名: f																		   *
	* 参数:																		       *
	*		n:   起始数字(0~9,递减的最大值)。									   *
	*		k:   当前计算的数字位数(0~20)。										   *
	*		a:   保存当前计算的21位数字。											   *
	*       res: 21位数字的乘方和。													   *
	* 返回值: 无。																	   *
	* 说明:																		       *
	*		排除尽可能多的数字,验证乘方和为21位数的数字。				               *
	***********************************************************************************/
	void f(INT32 n, INT32 k, INT8 *a, INT8 *res)
	{
		INT32 i,j;
		DOUBLE	temR =0;
	//	INT8 *p, *pTem;
	//	p = (char*)malloc(NUMW+2);
	//	pTem = (char*)malloc(NUMW+2);
	//	memset(p, 0, NUMW+2);
	//	memset(pTem, 0, NUMW+2);
		INT8 p[NUMW+2], pTem[NUMW+2];
	
		if(k==NUMW)	//已找到第21位数字?
			return;
	
		//将当前位循环赋值为小于n的数字
		for(i=n; i>=0; i--)
		{
			strcpy(pTem, res);
			addStr(res, nPow[i], p, NUMW+2);	//加入该位的乘方和
			if(nstrcmp(p, strMax) < 0)	//乘方和没超过21位数?
			{
				//当前数字初步符合条件
				a[k] = i + '0';
				strcpy(res, p);
				
				if(k==NUMW-1 && nstrcmp(res, strMin)>0) //找到第21位数字且该21位数乘方和为21位数字?
				{
					count++;
	
					//计算乘方和(res[j])中每位数字的乘方和
					memset(p, '\0', NUMW+2);
					for(j=0; j<NUMW; j++)
					{
						addStr(p, nPow[res[j]-'0'], p, NUMW+2);
					}
	
					if(nstrcmp(p, res) == 0)	//res[j]中每位数字的乘方和等于其本身?
					{
						//该数满足条件,输出
						printf("%s\n", res);	
					}
				}
	
				//递归计算下一位数字
				f(i, k+1, a, res);
			}
			strcpy(res, pTem);
		}
	
	//	free(p);
	//	free(pTem);
	}
	
	
	INT32 main(INT32 argc, INT32 *argv[])
	{
		INT8 str1[NUMW+1], str2[NUMW+1], strRes[2*NUMW+2];
		INT32 i, n, n1, n2, nRes;
		clock_t start, finish;
		DOUBLE duration;
		INT8 c2[2];
		INT8 res[2*NUMW+2];
	
		INT8 a[NUMW+1];
		memset(a, '\0', NUMW+1);
	
		str1[NUMW] = '\0';
		str2[NUMW] = '\0';
	
		memset(strRes, '\0', 2*NUMW+2);
		memset(res,'\0',  2*NUMW+2);
	
		srand((unsigned)time(NULL)); 
	
	
	
	/*
		//大数加法测试
		for(i=0; i<1000000; i++)
		{
			n1 = rand();
			n2 = rand();
	
			itoa(n1, str1, 10);
			itoa(n2, str2, 10);
	
	//		addStr(str1, str2, strRes);
			addStr(str1, str2, str1);
	
	//		nRes = atoi(strRes);
			nRes = atoi(str1);
			if(n1+n2 != nRes)
			{
	//			printf("%d + %d != %d\n", n1, n2, strRes);
				printf("%d + %d != %d\n", n1, n2, str1);
			}
			
		}
		*/
	
	/*
		//大数乘法测试
		for(i=0; i<1000000; i++)
		{
			n1 = rand();
			n2 = rand();
	
	//		n1 = 6473;
	//		n2 = 828;
	
			itoa(n1, str1, 10);
			itoa(n2, str2, 10);
	
	  		memset(strRes, 0, 2*NUMW+2);
	
			mulStr(str1, str2, strRes, 2*NUMW+2);
	//		mulStr(str1, str1, strRes, 2*NUMW+2);
	
			nRes = atoi(strRes);
	
			if(n1*n2 != nRes)
			{
				printf("%d * %d != %d\n", n1, n2, strRes);
			}
	
	//		if(n1*n1 != nRes)
	//		{
	//			printf("%d * %d != %d\n", n1, n1, strRes);
	//		}
			
		}
	*/
	
	/*
		//大数乘方测试
		for(i=0; i<1; i++)
		{
			n1 = rand()%10;
			n2 = rand()%10;
	
	//		n1 = 9;
	//		n2 = 21;
	
			itoa(n1, str1, 10);
			memset(strRes, 0, 2*NUMW+2);
			powStr(str1, n2, strRes);
	
			//容易超范围
			nRes = atoi(strRes);
	
			if(pow(n1, n2) != nRes)
			{
				printf("%d ^ %d != %d\n", n1, n2, strRes);
			}
			
		}
	*/
	
		n = NUMW;
		c2[1] = '\0';
		for(i=0; i<10; i++)
		{
			c2[0] = i + '0';
			powStr(c2, NUMW, nPow[i], NUMW+1); 
	
			printf("%d^%d = %s\n", i, n, nPow[i]);
		}
	
		strMin[0] = '1';
		strMax[0] = '1';
		for(i=1; i<NUMW; i++)
		{
			strMin[i] = '0';
			strMax[i] = '0';
		}
		strMax[i] = '0';
		strMin[i] = '\0';
		strMax[i+1] = '\0';
	
		res[0] = '0';
		res[1] = '\0';
	
		start = clock();
	
		printf("\nResult:\n");
	
		f(9, 0, a, res);
	
		finish = clock();
		duration = (DOUBLE)(finish-start) / CLOCKS_PER_SEC;
		printf("\n*****************\nf use %0.3f sec!\n", duration);
		printf("*****************\naddrStr use %0.3f sec!\n", duration_addStr);
	
		return 0;
	}

 


 

程序运行结果图:(CPUi5-2430M,操作系统:Windows 7 旗舰版):

可以看出程序总运行时间为111.243秒,符合题目要求。

F:\>"F:\蓝桥杯 程序设计_4.exe"

0^21 = 0

1^21 = 1

2^21 = 2097152

3^21 = 10460353203

4^21 = 4398046511104

5^21 = 476837158203125

6^21 = 21936950640377856

7^21 = 558545864083284007

8^21 = 9223372036854775808

9^21 = 109418989131512359209

Result:

449177399146038697307

128468643043731391252

*****************

f use 111.243 sec!

*****************

addrStr use 93.866 sec!

F:\>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值