7-13 水仙花数(20 分)

本文介绍如何使用C语言、Java及C++编写程序来找出所有N位的水仙花数,即一个N位正整数其每位数字的N次幂之和等于该数本身。文中提供详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

水仙花数是指一个N位正整数(N3),它的每个位上的数字的N次幂之和等于它本身。例如:1。 本题要求编写程序,计算所有N位水仙花数。

输入格式:

输入在一行中给出一个正整数N3)。

输出格式:

按递增顺序输出所有N位水仙花数,每个数字占一行。

输入样例:

3

输出样例:

153
370
371
407

一位自幂数:独身数
两位自幂数:没有
三位自幂数:水仙花数
四位自幂数:四叶玫瑰数
五位自幂数:五角星数
六位自幂数:六合数
七位自幂数:北斗七星数
八位自幂数:八仙数
九位自幂数:九九重阳数
十位自幂数:十全十美数


我的代码(C语言):

#include<stdio.h>
int main()
{
	int n,i,x=1,y=1;
	scanf("%d",&n);
	for(i=0;i<n-1;i++) x=x*10;
	for(i=0;i<n;i++) y=y*10;
	for(i=x;i<y;i++)
	{
		int t=i,sum=0;
		while(t!=0)
		{
			int tt=1,m=n;
			while(m--) tt=tt*(t%10);
			sum=sum+tt;
			t=t/10;
		}
		if(sum==i) printf("%d\n",i);
	}
	return 0;
}

我的代码(Java):

import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner In=new Scanner(System.in);
		int x=1,y=1;
		int n=In.nextInt();
		for(int i=0;i<n-1;i++) x=x*10;
		for(int i=0;i<n;i++) y=y*10;
		for(int i=x;i<y;i++)
		{
			int t=i,sum=0;
			while(t!=0)
			{
				int tt=1;
				for(int j=0;j<n;j++) tt=tt*(t%10);
				sum=sum+tt;
				t=t/10;
			}
			if(sum==i) System.out.printf("%d\n", i);
		}
	}
}
我的代码(C++):

#include<iostream>
using namespace std;
int main()
{
	int n,i,x=1,y=1;
	cin>>n;
	for(i=0;i<n-1;i++) x=x*10;
	for(i=0;i<n;i++) y=y*10;
	for(i=x;i<y;i++)
	{
		int t=i,sum=0;
		while(t!=0)
		{
			int tt=1,m=n;
			while(m--) tt=tt*(t%10);
			sum=sum+tt;
			t=t/10;
		}
		if(sum==i) cout<<i<<endl;
	}
	return 0;
}

<think>嗯,用户想用C++的深度优先搜索(DFS)算法来找N位水仙花。首先,我需要回忆一下水仙花的定义。水仙花,也就是阿姆斯特朗,是指一个N位,其各位字的N次方之和等于该本身。例如,153是一个3位水仙花,因为1³ + 5³ + 3³ = 153。 接下来,我得考虑如何用DFS来生成所有可能的N位组合,然后验证它们是否符合条件。DFS通常用于遍历或搜索树形结构,这里可能需要生成每一位的字,并递归地构建整个。需要注意,第一位不能为0,所以当N大于1时,第一位应该从1开始。 然后,考虑如何优化计算过程。每次递归时,可能需要计算当前位的N次方,为了避免重复计算,可以预先计算0-9的N次方并存储起来,这样在需要的时候直接取值,提高效率。 在实现DFS时,需要维护当前路径(已经选择的字),以及当前的和(各位字的N次方之和)。当路径长度达到N时,检查该和是否等于由路径构成的。如果相等,就记录下来。 举个例子,假设N=3,DFS会从第一位开始,选1-9,然后第二位选0-9,第三位选0-9,每次计算三者的立方和,然后组合成三位,判断是否等于和。这样遍历所有可能的三位组合。 另外,需要注意剪枝。比如,在递归过程中,如果当前的和已经超过了可能的最大N位(比如999的三次方和是3*9³=2187,而三位最大是999,但这里可能需要更精确的判断),但水仙花的定义是各位N次方的和等于原,所以当路径长度未满N位时,可能无法提前剪枝,因此可能需要生成所有可能的组合后再验证。 不过,对于较大的N,比如N=7,这样的方法可能会比较慢,因为要生成所有7的组合,但水仙花实际上只在某些N存在,比如已知的有3、4、5、6位等,但可能对于更大的N不存在解,所以需要预先了解这一点,避免不必要的计算。 然后,关于C++的实现,需要处理大的问题。当N较大时,各位字的N次方之和可能超过普通整类型的范围,所以可能需要使用更大的据类型,比如unsigned long long。 接下来,编写DFS函。参可能包括当前位、当前的和、当前的值(由路径构成的),以及路径中的各个字。当当前位等于N时,检查是否满足条件。 例如,路径是各个字的组,比如[1,5,3],对应的值是153,而和是1³+5³+3³=153,这时就输出这个。 在递归过程中,每一位的选择范围是0-9,但第一位不能为0。所以,当递归到第一位时,循环从1开始,其余位从0开始。 然后,预计算0-9的N次方,存储在一个组中,比如pow_cache[i] = i^N。 这样,在计算和的时候,可以直接取用,而不需要重复计算幂次。 现在,整合这些思路,写出C++的代码结构。可能的结构包括: 1. 预计算pow_cache。 2. DFS递归生成字组合。 3. 当组合完成时,检查是否满足条件。 4. 收集所有满足条件的并输出。 代码的大致步骤可能如下: - 输入N。 - 预计算0-9的N次方。 - 初始化路径,比如一个组或字符串,记录当前选择的字。 - 开始DFS递归,从第0位开始,如果是第0位且N>1,则从1开始循环,否则从0开始。 - 每次选择一位字,加入路径,并累加其N次方到当前和。 - 当路径长度达到N时,检查当前和是否等于由路径构成的值。如果是,则输出。 - 回溯,移除最后一位字,继续尝试其他可能。 例如,对于N=3: 预计算pow_cache[10] = {0,1,8,27,...,729}。 DFS函可能像这样: void dfs(int pos, unsigned long long current_sum, unsigned long long number, vector<int>& digits) { if (pos == N) { if (current_sum == number) { // 输出number } return; } int start = (pos == 0 && N > 1) ? 1 : 0; for (int i = start; i <= 9; ++i) { digits.push_back(i); unsigned long long next_sum = current_sum + pow_cache[i]; unsigned long long next_number = number * 10 + i; dfs(pos + 1, next_sum, next_number, digits); digits.pop_back(); } } 这样,每次递归调用处理一位字,累加和,并构建当前值。当pos等于N时,检查是否满足条件。 但是,这里存在一个问题:当N较大时,number可能会溢出。例如,当N=39时,number是一个39位,远远超过unsigned long long的范围(最大是18446744073709551615,约1e19)。所以,这种情况下,无法用普通的整类型存储number,必须采用字符串或其他方式处理。但水仙花的定义中,number的各位字的N次方之和必须等于该本身。所以,当N较大时,计算和可能需要处理极大的值,这可能导致溢出问题。 不过,根据已知的水仙花列表,较大的N可能不存在解,或者解的值较小,所以可能不需要处理非常大的N。但为了正确性,应该在代码中处理大的情况。然而,这可能会增加复杂度。另一种方法是,当计算current_sum时,同时构建number的字符串形式,最后将current_sum与该字符串转换成的值比较。但转换大字符串到值同样可能溢出。因此,可能需要在比较时,将current_sum转换为字符串,与构建的number字符串比较,而不是用值比较。例如,对于N=3,current_sum是153,而number是153,所以可以直接比较。但如果current_sum是某个大,而number的字符串形式转换成值可能溢出,所以必须将两者都作为字符串处理。 这样,问题变得复杂。或者,可以假设N的范围较小,不会溢出,比如N<=19,这样unsigned long long可以处理到18,446,744,073,709,551,615,即最多19位。但水仙花是否存在较大的N位呢?例如,已知的5位水仙花有54748,92727等,这些都是5位值在unsigned long long范围内。可能对于更大的N,比如20位,代码会溢出,但实际应用中可能只需要处理较小的N。 因此,在代码中,假设N不会太大,可以使用unsigned long long来存储number和current_sum。如果用户输入的N过大,可能导致溢出,但这种情况可能无法处理,需要给出提示,或者改用大库。 综上,C++代码的大致步骤是: 1. 预计算0-9的N次方,存储在pow_cache组中。 2. 使用DFS生成所有N位,每一位从0-9中选择(首位非0)。 3. 在递归过程中,维护当前的和以及当前的值。 4. 当递归到N位时,比较和与值是否相等,相等则输出。 测试时,例如输入N=3,应该输出153、370、371、407等。 现在,需要考虑如何将路径转换为值。例如,在DFS过程中,每次选择一位,可以逐步构建值。例如,初始值是0,每次乘以10并加上当前字。这样,当路径为1,5,3时,值的计算过程是0*10+1=1,1*10+5=15,15*10+3=153。这和sum的累积是同时进行的。 所以,在代码中,参包括当前的值(由路径构成的),以及当前的和(各位的N次方之和)。当pos等于N时,检查两者是否相等。 现在,编写代码的大纲: #include <iostream> #include <vector> #include <cmath> using namespace std; int N; vector<unsigned long long> pow_cache; void dfs(int pos, unsigned long long current_sum, unsigned long long number, vector<int>& digits) { if (pos == N) { if (current_sum == number) { cout << number << endl; } return; } int start = (pos == 0) ? 1 : 0; // 首位不能为0,当N>1时,首位从1开始。如果N=1,则允许0? // 但是当N=1时,水仙花是0-9,因为每个1位的各位1次方之和等于自身。 // 所以需要处理N=1的情况。 if (pos == 0 && N > 1) { start = 1; } else { start = 0; } for (int i = start; i <= 9; ++i) { digits.push_back(i); unsigned long long next_sum = current_sum + pow_cache[i]; unsigned long long next_number = number * 10 + i; dfs(pos + 1, next_sum, next_number, digits); digits.pop_back(); } } int main() { cin >> N; // 预计算0-9的N次方 pow_cache.resize(10); for (int i = 0; i < 10; ++i) { pow_cache[i] = pow(i, N); // 注意pow函返回的是double,可能需要转换为unsigned long long // 或者使用循环计算幂次,避免精度问题 } // 处理N=0的情况?通常N>=1 if (N <= 0) { cout << "Invalid input N" << endl; return 0; } vector<int> digits; dfs(0, 0, 0, digits); return 0; } 不过,这里有几个需要注意的地方: 1. pow函返回的是double类型,当i^N较大时可能会有精度丢失。例如,9^20用double表示可能会有误差。因此,应该手动计算幂次,使用循环乘法,避免使用pow函。 修改预计算的部: for (int i = 0; i < 10; ++i) { unsigned long long val = 1; for (int j = 0; j < N; ++j) { val *= i; } pow_cache[i] = val; } 这样可以避免pow的精度问题。 2. 当N=1时,所有0-9的都是水仙花,因为1^1=1,所以代码应该输出0-9。但根据当前代码,当N=1时,首位允许0,所以会生成0-9。例如,当pos=0,N=1,start=0吗?根据代码中的条件: if (pos ==0 && N>1) start=1,否则start=0。当N=1时,start=0,所以i从0开始循环,正确生成0-9。 测试N=1时,应该输出0-9。 3. 对于较大的N,比如N=39,计算pow_cache[i]可能会溢出unsigned long long,导致错误。例如,9^39是一个很大的,远超unsigned long long的范围。此时,预计算的值会溢出,导致错误的结果。因此,代码需要处理这种情况,或者限制N的范围。但在实际应用中,水仙花的存在性可能只在较小的N中存在,所以用户可能需要输入合理的N。 综上,正确的C++代码应该包含预计算幂次,使用DFS生成所有可能的N位,并检查各位的N次方之和是否等于值本身。同时处理首位不能为0的情况(当N>1时)。 现在,编写完整的代码,并测试示例。 例如,当输入N=3时,输出应为153、370、371、407。 另一个测试用例是N=4,输出1634、8208、9474。 在代码中,还需要注意在N=1时输出0-9。 现在,用户的问题是关于如何使用DFS实现,因此代码的结构需要符合DFS的方式,生成所有可能的字组合,并在递归过程中维护和与值。</think>要使用深度优先搜索(DFS)算法在C++中找到所有N位水仙花,可以按照以下步骤实现: ### 实现步骤 1. **预处理幂次值**:预先计算0-9的N次方并存储,避免重复计算。 2. **DFS递归生成字组合**:从高位到低位生成每一位字,首位不能为0(N>1时)。 3. **剪枝与验证**:当生成完整的N位时,检查其各位字的N次方之和是否等于该本身。 ### C++代码实现 ```cpp #include <iostream> #include <vector> using namespace std; int N; vector<unsigned long long> power_cache; void dfs(int pos, unsigned long long current_sum, unsigned long long current_num, vector<int>& path) { if (pos == N) { if (current_sum == current_num) { cout << current_num << endl; } return; } int start = (pos == 0 && N > 1) ? 1 : 0; for (int i = start; i <= 9; ++i) { path.push_back(i); dfs(pos + 1, current_sum + power_cache[i], current_num * 10 + i, path); path.pop_back(); } } int main() { cin >> N; if (N <= 0) { cerr << "N必须为正整" << endl; return 1; } // 预计算0-9的N次方 power_cache.resize(10, 0); for (int i = 0; i < 10; ++i) { unsigned long long val = 1; for (int j = 0; j < N; ++j) { val *= i; } power_cache[i] = val; } vector<int> path; dfs(0, 0, 0, path); return 0; } ``` ### 代码说明 1. **预处理幂次值**:通过循环计算每个字的N次方,避免重复计算。 2. **DFS递归**: - `pos`表示当前处理到第几位(从0开始)。 - `current_sum`是当前路径中各位字的N次方之和。 - `current_num`是当前路径构成的值。 - 当`pos == N`时,验证是否满足水仙花条件。 3. **首位处理**:若N>1,首位从1开始遍历;否则允许首位为0(N=1)。 例如,当输入$N=3$时,程序会输出: ``` 153 370 371 407 ``` 这些值满足$abc = a^3 + b^3 + c^3$[^1]。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值