试题:打印n位水仙花数

46 篇文章 0 订阅

水仙花数指的是一个n位数(n≥3), 它的每个位上的数字的n次幂之和等于它本身.(例如:1^3 + 5^3 + 3^3 = 153) , 理论上, 最大的水仙花数不超过34位

题目还是从 luciferisnotsatan 博客上看到的, 他的实现见:n位水仙花数

由于C++中整数的取值范围受限, 所以下面的代码最大只能计算到n = 19的情形

下面是我的实现代码:

#include <iostream>
using namespace std;

void show(int max_bits)
{
    typedef unsigned long long long_type; // for gcc
//  typedef unsigned __int64 long_type; // for vc and vs

    if (max_bits > 34) {
        max_bits = 34;
    }

	long_type base[10] =
	{
		0, 1, 8, 27, 64, 125, 216, 343, 512, 729
	};

	int number[34] = { 0 };
	for (int i = 3; i <= max_bits; ++i) {
		int count[10] = { 0 };
		for (int j = 0; j < i; ++j) {
			number[j] = 0;
			++count[0];
		}

		while (true) {
			int count_copy[10] = { 0 };
			for (int j = 0; j < 10; ++j) {
				count_copy[j] = count[j];
			}

			long_type value = 0;
			for (int j = 0; j < i; ++j) {
				value += base[number[j]];
			}

			bool equal = true;
			long_type value_copy = value;
			do {
				if (--count_copy[value_copy % 10] < 0) {
					equal = false;
					break;
				}
				value_copy /= 10;
			} while (value_copy > 0);

			if (equal) {
				for (int j = 0; j < 10; ++j) {
					if (0 != count_copy[j]) {
						equal = false;
						break;
					}
				}
			}

			if (equal) {
				cout << i << ": " << value << endl;
			}

			if (number[i - 1] < 9) {
				--count[number[i - 1]];
				++count[++number[i - 1]];
			}
			else {
				int check_index = i - 2;
				while (check_index >= 0 && 9 == number[check_index]) {
					--check_index;
				}

				if (check_index < 0) {
					break;
				}
				else {
					--count[number[check_index]];
					++count[++number[check_index]];
					int new_number = number[check_index];
					for (int j = check_index + 1; j < i; ++j) {
						number[j] = new_number;
						--count[9];
						++count[new_number];
					}
				}
			}
		}

		for (int j = 0; j < 10; ++j) {
			base[j] *= j;
		}
	}
}

int main()
{
	show(19);
    return 0;
}

从代码量上, 我的比他的要少得多, 但效率不及他的, 我的在算16时就能明显感觉到等待了, 19时出来的第一个就等了2秒, 后面倒很快

我的出数据的顺序与百度百科上的相同, 估计算法相似, 另外百度百科中在19时少帖了一项

这次用到的手法又与 试题:网易笔试的一道题目 相似, 这个手法屡试不爽

想到value的计算是可优化的, 优化了下, 虽然现在还是要花8.5秒, 但相比前代码已减少2.5秒了:

#include <iostream>
using namespace std;

void show(int max_bits)
{
    typedef unsigned long long long_type; // for gcc
//  typedef unsigned __int64 long_type; // for vc and vs

    if (max_bits > 34) {
        max_bits = 34;
    }

	long_type base[10] =
	{
		0, 1, 8, 27, 64, 125, 216, 343, 512, 729
	};

	for (int i = 3; i <= max_bits; ++i) {
	    long_type value = 0;
	    int number[34] = { 0 };
		int count[10] = { i };

		while (true) {
			int count_copy[10] = { 0 };
			for (int j = 0; j < 10; ++j) {
				count_copy[j] = count[j];
			}

			bool equal = true;
			long_type value_copy = value;
			do {
				if (--count_copy[value_copy % 10] < 0) {
					equal = false;
					break;
				}
				value_copy /= 10;
			} while (value_copy > 0);

			if (equal) {
				for (int j = 0; j < 10; ++j) {
					if (0 != count_copy[j]) {
						equal = false;
						break;
					}
				}
			}

			if (equal) {
				cout << i << ": " << value << endl;
			}

			if (number[i - 1] < 9) {
				--count[number[i - 1]];
				value -= base[number[i - 1]];
				++number[i - 1];
				++count[number[i - 1]];
				value += base[number[i - 1]];
			}
			else {
				int check_index = i - 2;
				while (check_index >= 0 && 9 == number[check_index]) {
					--check_index;
				}

				if (check_index < 0) {
					break;
				}
				else {
					--count[number[check_index]];
					value -= base[number[check_index]];
					++number[check_index];
					++count[number[check_index]];
					value += base[number[check_index]];

					int new_number = number[check_index];
					int change_count = i - check_index - 1;
					count[9] -= change_count;
					count[new_number] += change_count;
					value -= change_count * (base[9] - base[new_number]);
					for (int j = check_index + 1; j < i; ++j) {
						number[j] = new_number;
					}
				}
			}
		}

		for (int j = 2; j < 10; ++j) {
			base[j] *= j;
		}
	}
}

int main()
{
	show(19);
    return 0;
}

再优化下:去除部分不可能的组合(过大和过小), 大约可节省1/10 - 1/4的组合尝试,  现用时7.5秒, 相对前代码, 节约1秒

#include <iostream>
using namespace std;

void show(int max_bits)
{
    typedef unsigned long long long_type; // for gcc
//  typedef unsigned __int64 long_type; // for vc and vs

    if (max_bits > 34) {
        max_bits = 34;
    }

	long_type base[10] =
	{
		0, 1, 8, 27, 64, 125, 216, 343, 512, 729
	};

    long_type lower_bound = 100;
    long_type upper_bound = 1000;

	for (int i = 3; i <= max_bits; ++i) {
	    long_type value = 0;
	    int number[34] = { 0 };
		int count[10] = { 0 };

		int index = 0;
        long_type upper_tmp = upper_bound;
        for (int j = 9; j >= 1; --j) {
            int num = upper_tmp / base[j];
            while (num > 0 && index < i) {
                number[index] = j;
                ++count[j];
                value += base[j];
                --num;
                ++index;
            }
            if (index >= i) {
                break;
            }
            upper_tmp %= base[j];
        }
        count[0] += (i - index);

        int min = 0;
        long_type avg = lower_bound / i;
        for (int j = 8; j >= 1; --j) {
            if (avg > base[j]) {
                min = j + 1;
                break;
            }
        }

		while (true) {
			int count_copy[10] = { 0 };
			for (int j = 0; j < 10; ++j) {
				count_copy[j] = count[j];
			}

			bool equal = true;
			long_type value_copy = value;
			do {
				if (--count_copy[value_copy % 10] < 0) {
					equal = false;
					break;
				}
				value_copy /= 10;
			} while (value_copy > 0);

			if (equal) {
				for (int j = 0; j < 10; ++j) {
					if (0 != count_copy[j]) {
						equal = false;
						break;
					}
				}
			}

			if (equal) {
				cout << i << ": " << value << endl;
			}

			if (number[i - 1] > 0) {
				--count[number[i - 1]];
				value -= base[number[i - 1]];
				--number[i - 1];
				++count[number[i - 1]];
				value += base[number[i - 1]];
			}
			else {
				int check_index = i - 2;
				while (check_index >= 0 && 0 == number[check_index]) {
					--check_index;
				}

				if (check_index < 0) {
					break;
				}
				else {
					--count[number[check_index]];
					value -= base[number[check_index]];
					--number[check_index];
					++count[number[check_index]];
					value += base[number[check_index]];

					int new_number = number[check_index];
					int change_count = i - check_index - 1;
					count[0] -= change_count;
					count[new_number] += change_count;
					value += change_count * (base[new_number] - base[0]);
					for (int j = check_index + 1; j < i; ++j) {
						number[j] = new_number;
					}

                    if (number[0] < min) {
                        break;
                    }
				}
			}
		}

		for (int j = 2; j < 10; ++j) {
			base[j] *= j;
		}

        lower_bound *= 10;
        upper_bound *= 10;
	}
}

int main()
{
	show(19);
    return 0;
}


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值