6秒求出21 位的花朵数(水仙花数)

58 篇文章 0 订阅
30 篇文章 1 订阅


方法:砍掉小于21位的数,砍掉大于21位的数,对21位的数其中每个数字出现的次数进行比较相同则为花朵数。每一次用9的21次方依次试探,大于或小于21位的去掉,那么这里不仅仅去掉 了2个数,而是把大于这个数或者小于这个数 的所有可能情况都咔嚓了,这里是一个很重要的点,这里进行的减枝是整个算法的关键。


#include <stdio.h>
#include <time.h>
#include <string.h>
#define    N 10
/*各个数的21次方后的值*/
int result[20][21]={0};
int length = 0; //记录符合条件的数的个数
const int Pow[10][21] = {{0}, {1}, {2, 5, 1, 7, 9, 0, 2},
						{3, 0, 2, 3, 5, 3, 0, 6, 4, 0, 1},
                        {4, 0, 1, 1, 1, 5, 6, 4, 0, 8, 9, 3, 4},
                        {5, 2, 1, 3, 0, 2, 8, 5, 1, 7, 3, 8, 6, 7, 4},
                        {6, 5, 8, 7, 7, 3, 0, 4, 6, 0, 5, 9, 6, 3, 9, 1, 2},
                        {7, 0, 0, 4, 8, 2, 3, 8, 0, 4, 6, 8, 5, 4, 5, 8, 5, 5},
                        {8, 0, 8, 5, 7, 7, 4, 5, 8, 6, 3, 0, 2, 7, 3, 3, 2, 2, 9},
                        {9, 0, 2, 9, 5, 3, 2, 1, 5, 1, 3, 1, 9, 8, 9, 8, 1, 4, 9, 0, 1}};

//进行叠加运算
void add (int  *sum, const int    num)
{
    int        i;
    for (i = 0; i < 21; i++)
        sum[i] += Pow [num][i];
    for (i = 0; i < 21; i++)
    {
        if (sum[i] > 9)
        {
            sum[i + 1] += sum[i] / 10;
            sum[i] = sum[i] % 10;
        }
    }
}
// 减去当前试探值的次方 
void sub (int    *sum, const int num)
{
    int        i;
    for (i = 0; i < 21; i++)
    {
        if (sum[i] < Pow[num][i])
        {
            sum[i + 1] -= 1;
            sum[i] = sum[i] + 10 - Pow[num][i];
        }
        else
        {
            sum[i] -= Pow[num][i];
        }
    }
}
void SumNumber (int *sum, int * pstack)
{
    int len = 21;
    memset (pstack, 0, sizeof (int) * N);
    while (len--)
       pstack[*sum++]++;
}
int cmp (int *a, int *b)
{
    int i;
    for (i = 0; i < N; i++)
        if (a[i] != b[i])
            return 0;
    return 1;
}
void narciss ()
{
    int iSumNum[N] = {0}; //用来存放0~9
    int iStackNum[N] = {0}; //用来存放0~9这十个数出现的次数
    int iSum[22] = {0};   // 用来存放每次的计算值
    int iStack[22] = {0};//iStack存放的是总试探值 iStack中值的21次方数就是iSum中的值
    int *top = iStack;
    int *const maxTop = iStack + 21;
    int flag = 0;
    int k = 9; // 试探值
    int i, sum = 0;
    while (1)
    {
        if (top < maxTop && iSum[21] == 0) // 如果没有21位就继续累加
        {
            add (iSum, k);
            *top++ = k;
            iStackNum[k]++;
        }
        if (iSum[21] > 0) // 如果超过21位 就进行减操作
        {
            sub (iSum, k);
            iStackNum[k]--;
            top--;
            k = *top - 1;
        }
        if (top == maxTop) // 如果21位就进行判断
        {
            if (0 == iSum[20]) // 最高位为0 就表示没有这样的数存在
                break;
			
	//		for(sum = 0, i = 0; i < 21; i++)
	//			sum += iStack[i];
	//		if((sum-iSum[0]) % 10 == 0)
	//		{
			//	printf("sum = %d\n",sum);
				/*iSum 是当前的值 iSumNum 是记录每个数出现的次数*/
				SumNumber (iSum, iSumNum);  
	
				// 对比每个数出现的次数是否一样
				if (cmp (iSumNum, iStackNum))
				{
					for (i = 20; i >= 0; i--)
						result[length][i] = iSum[i];
					length++;	
				}
	//		}			
			// 改变试探值 如果不等于0 就在当前位置进行降低试探
            if (0 != *(top - 1))
            {
                sub (iSum, k);
                iStackNum[k]--;
            }else
            {
				// 找到下一个可以降低试探的位置
                while (0 == *(top - 1))
                {
                    top--;
                    iStackNum[0]--;
                }
                flag = 1;
            }
            
            k = *--top;
            if (flag)
            {
                sub (iSum, k);
                iStackNum[k]--;
                flag = 0;
            }
            k--;
        }
    }
}
int main ()
{
    narciss ();
	//从小到大排序输出
	
	for(int i = length-1; i >= 0; i--)
	{
		for(int j = 20; j >= 0; j--)
			printf("%d",result[i][j]);
		printf("\n");
	}
    printf ("%.2lf s\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值