方法:砍掉小于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;
}