求任意位数的水仙花数(阿姆斯特朗数)

首先,什么是水仙花数呢?水仙花数又叫阿姆斯特朗数。具体是什么,请看链接http://baike.baidu.com/link?url=EBzzLpQqAIeFTBaOCwciWJGB0wep4kFHuG8iPg8kQcPk6L7umAFTkp-mAsMHxPoE


大概就是说,一个数有n位,这个数的每一位上的数的n次方之和等于这个数,这个数就是水仙花数。

大一的时候做过这个题,给一个位数,让求这个位数里面的水仙花数。但是因为给的位数都比较小,所以暴力之。

昨天偶然看到一个让求21位,程序要在一分钟之内完成。如果暴力,1分钟肯定不够。最后想到的方法就是:统计0——9出现的次数,算出每种情况的值,看这个值各位上0——9出现的次数是否满足用来算的情况  。这样时间就大大降低了。不过,21位依然跑了45s。(不知道还有木有更好的方法。请不要告诉我百度枚举。。。。我不再相信百度了,居然少了几个,自己程序跑居然多跑了几个出来。。。我还以为bug了呢,心想这TM要如何调试啊。结果wiki了一下,百度果然是个渣渣。不吐槽了O(∩_∩)O~)

#include<stdio.h>
#include<string.h>
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define DIGIT 21//每次就只用改变这里,就可以算出不同位数的水仙花数了。如果要想算出所用的,这里就写最大,然后再在程序里加一层循环就是咯

int Count[10];//Count用来保存枚举是0——9出现的次数,用以和算出来的值各数字出现次数进行对比。
int cnt1 ,num1[10][DIGIT+1][DIGIT+1];//cnt1是符合条件的个数。num1用来保存0——9分别出现0——DIGIT次对应的答案
char ans[10][DIGIT+1];//保存符合条件的答案
//这两个就是为了排序方便一点
vector<string> v;
string s[10];

void deal()
{
	int cnt[10];//保存算出来的数0——9出现的次数,用以和Count对比看是否满足条件
	int no[DIGIT+1];//算出来的数
	memset(no,0,sizeof(no));
	memset(cnt,0,sizeof(cnt));
	for(int k = 1;k<10;k++)
	{
		if(num1[k][Count[k]][DIGIT]!=0)
		{
			return;
		}
		for(int i = 0;i < DIGIT;i++)
		{
			no[i] += num1[k][Count[k]][i];
			if(no[i] > 9)
			{
				no[i+1] += (no[i] / 10);
				no[i] %= 10;
			}
		}
	}
	if(no[DIGIT]!=0)
	{
		return;
	}
	if(no[DIGIT-1]!=0)
	{
		int flag = 0;
		for(int j = 0;j < DIGIT;j++)
		{
			cnt[no[j]]++;
		}
		for(int j = 0;j<10;j++)
		{
			if(cnt[j]!=Count[j])
			{
				flag = 1;
				break;
			}
		}
		if(!flag)
		{
			ans[cnt1][DIGIT] = '\0';
			for(int j = 0 ,k = DIGIT - 1;j < DIGIT;j++,k--)
			{
				ans[cnt1][k] = no[j] + '0';
			}
			s[cnt1] = ans[cnt1];
			v.push_back(s[cnt1]);
			cnt1++;
		}
	}
};

int main()
{
	//计算从0——9出现分别0——DIGIT次时的值
	for(int i = 1;i<10;i++)
	{
		num1[i][1][0] = 1;
		int index = 0;
		for(int j = 1;j<=DIGIT;j++)
		{
			for(int r = 0;r<=index;r++)
			{
				num1[i][1][r] *= i;
			}
			for(int r = 0;r<=index;r++)
			{
				if(num1[i][1][r] > 9)
				{
					num1[i][1][r+1] += (num1[i][1][r] / 10);
					num1[i][1][r] %= 10;
				}
			}
			while(index < DIGIT-1 && num1[i][1][index+1] > 0)
			{
				index++;
				if(num1[i][1][index] > 9)
				{
					num1[i][1][index+1] += (num1[i][1][index] / 10);
					num1[i][1][index] %= 10;
				}
			}
		}
		for(int j = 2;j<=DIGIT;j++)
		{
			for(int r = 0;r<=DIGIT;r++)
			{
				num1[i][j][r] = num1[i][1][r] * j;
			}
			for(int r = 0;r<DIGIT;r++)
			{
				if(num1[i][j][r] > 9)
				{
					num1[i][j][r+1] += (num1[i][j][r] / 10);
					num1[i][j][r] %= 10;
				}
			}
		}
	}
	//枚举0——9分别出现0——DIGIT次的情况,0——9分别对应a——j
	for(int a = 0;a<=DIGIT;a++)
	{
		Count[0] = a;
		for(int b = 0;b<=DIGIT;b++)
		{
			if(a + b > DIGIT)//保证出现的次数不大于DIGIT,下同
			{
				break;
			}
			Count[1] = b;
			for(int c = 0;c<=DIGIT;c++)
			{
				if(a + b + c > DIGIT)
				{
					break;
				}
				Count[2] = c;
				for(int d = 0;d<=DIGIT;d++)
				{
					if(a + b + c + d > DIGIT)
					{
						break;
					}
					Count[3] = d;
					for(int e = 0;e<=DIGIT;e++)
					{
						if(a + b + c + d + e > DIGIT)
						{
							break;
						}
						Count[4] = e;
						for(int f = 0;f<=DIGIT;f++)
						{
							if(a + b + c + d + e + f > DIGIT)
							{
								break;
							}
							Count[5] = f;
							for(int g = 0;g<=DIGIT;g++)
							{
								if(a + b + c + d + e + f + g > DIGIT)
								{
									break;
								}
								Count[6] = g;
								for(int h = 0;h<=DIGIT;h++)
								{
									if(a + b + c + d + e + f + g + h> DIGIT)
									{
										break;
									}
									Count[7] = h;
									for(int i = 0;i<=DIGIT;i++)
									{
										if(a + b + c + e + f + g + h + i > DIGIT)
										{
											break;
										}
										Count[8] = i;
										int j = DIGIT - a - b - c - d - e - f - g - h - i;
										if(j < 0)
										{
											break;
										}
										Count[9] = j;
										deal();
									}
								}
							}
						}
					}
				}
			}
		}
	}
	//排序,将答案从小到大输出
	sort(v.begin(),v.end());
	for(int i = 0;i<v.size();i++)
	{
		cout<<v[i]<<endl;
	}
	return 0;
}

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值