蓝桥杯 花朵数 解题报告
2015-02-25 21:20
537 查看
一个N位的十进制正整数,如果它的每个位上的数字的N次方的和等于这个数本身,则称其为花朵数。
例如:
当N=3时,153就满足条件,因为 1^3 + 5^3 + 3^3 = 153,这样的数字也被称为水仙花数(其中,“^”表示乘方,5^3表示5的3次方,也就是立方)。
当N=4时,1634满足条件,因为 1^4 + 6^4 + 3^4 + 4^4 = 1634。
当N=5时,92727满足条件。
实际上,对N的每个取值,可能有多个数字满足条件。
程序的任务是:求N=21时,所有满足条件的花朵数。注意:这个整数有21位,它的各个位数字的21次方之和正好等于这个数本身。
如果满足条件的数字不只有一个,请从小到大输出所有符合条件的数字,每个数字占一行。因为这个数字很大,请注意解法时间上的可行性。
这次我重新修改一下这篇文章,之前的内容在下面,一开始,我是直接按照大数运算的方法,从10000......到99999........暴力枚举,虽然程序没有出结果,但是我将正确的答案带进去发现计算模块和验证模块是正确的,于是就心满意足了,也没有验证一下计算出结果到底需要多久,今天看了一个教学视频,恍然大悟,回想一下以前的分析算法复杂度,CPU的运算速度为10^9次/秒,而要计算的数超过10^21个,就算纯粹的枚举数字,都组要10^12秒,换算下来接近31709年。。。在我有生之年是看不到结果了。。。所以这次换了一个方法,个人感觉很巧妙,原代码是java版的,还好鄙人学了一个学期的java,研究了一下,下面说说分析过程。
首先,我们任然使用之前的大数系统,也就是通过字符串来表示大数。因为题目要求是说每一位的21次方之和,也就是说,这个和实际上跟数字的顺序并没有关系,举个简单的例子,123和321的每一位的3次方之和并没有区别,然后我们就可以换一个角度来考虑这个问题,如果纯粹的枚举数字,必定会出现大量的重复,为了避免重复,我们直接枚举每一个数字出现的次数,比如0出现3次,1出现2次,2出现4次。。。如果能很好的处理这个排列组合,使之不发生重复,那效率提高了不是一点点。
关键问题是怎样模拟这个排列呢,总不能用10个for循环吧,那种太low了,在循环太多的情况下,我们用什么方法呢?没错,就是递归。
在递归的时候,我们要枚举从0到9这10个数每一个数出现的次数,递归出口当然就是枚举到9时,因为总位数为21位,只要前9个数出现的次数确定了,那第十个数(9)出现的次数也就确定了,就是21-used,当获得了一组数据nn[]之后,我们将这组数据所代表的和sum[]求出来,这里注意,因为枚举的是每一个数字出现的次数,所以我们并不知道那个21位数到底是几,但是,我们可以通过nn[]这组数据,把一个21位的空数组num[]填满,不用考虑顺序,填满之后,我们把sum和num进行排序,如果这两个数组的每一位都相等,那说明这个sum的值就是我们要求的值,输出即可。
这个代码在我i7的本子上运行80+s,还算可以了
#include<iostream>
#include<algorithm>
#include<memory.h>
#include<cstring>
using namespace std;
int square[10][21];
int num[21]={0};
int sum[21]={0};
void carry(int *t)
{
int i;
for(i=19;i>=0;i--)
{
t[i]=t[i]+t[i+1]/10;
t[i+1]=t[i+1]%10;
}
}
void add(int *a,int *b)
{
int i;
for(i=0;i<=20;i++)
{
a[i]=a[i]+b[i];
}
carry(a);
}
void mul(int *a,int b)
{
int i;
for(i=0;i<21;i++)
a[i]=a[i]*b;
carry(a);
}
void showNum(int *n)
{
int i;
for(i=0;i<21;i++)
cout<<n[i];
}
void init()
{
int i,j;
for(i=0;i<10;i++)
{
for(j=0;j<20;j++)
{
square[i][j]=0;
}
square[i][20]=i;
}
for(i=0;i<10;i++)
{
for(j=0;j<20;j++)
{
mul(square[i],i);
}
}
}
int judge()
{
int i;
int temp[21];
for(i=0;i<21;i++)
temp[i]=sum[i];
sort(temp,temp+21);
sort(num,num+21);
for(i=0;i<21;i++)
{
if(num[i]!=temp[i])
return 0;
}
return 1;
}
void getSum(int *nn)
{
int i,j,p;
p=0;
for(i=0;i<10;i++)
{
for(j=0;j<nn[i];j++)
{
add(sum,square[i]);
num[p++]=i;
}
}
if(sum[0]<10&&judge())
{
if(sum[0]>0)
{
showNum(sum);
cout<<endl;
}
}
memset(sum,0,sizeof(sum));
}
void dfs(int *nn,int pos,int used)
{
if(pos==9)
{
nn[9]=21-used;
getSum(nn);
return;
}
for(int i=0;i<=21-used;i++)
{
nn[pos]=i;
dfs(nn,pos+1,used+i);
}
}
int main()
{
int i,j;
int nn[10]={0};
init();
dfs(nn,0,0);
return 0;
}
下面的方法适合在银河那样的超级计算机上运行,土豪专用。。。
没有想到什么好的解题方法,就是暴力枚举,不过枚举这么多数花的时间确实不少,关键是21位数的运算没有什么好用的数据类型,只能按照大数来处理,既然是大数,当然需要要用字符串来表示,这无疑更加降低了运算速度,然后要预处理一下,就是把0-9的21次方都事先算出来,不过这个高精度运算同样要按照大数原则运算,否则位数太长,然后很不情愿的又要编一个大数乘法函数,函数中所有的数都应该按照21位处理,虽然浪费点内存,但代码清晰,最后就是把每一位数的21次方相加,看得到的和是否跟那个数一样,因此还需要一个判断函数,就这样,我写出了程序,但很明显需要优化,因为运算出结果所花的时间太长了,不过我验证了结果,是正确的,希望大牛能想出更加高效的算法。
例如:
当N=3时,153就满足条件,因为 1^3 + 5^3 + 3^3 = 153,这样的数字也被称为水仙花数(其中,“^”表示乘方,5^3表示5的3次方,也就是立方)。
当N=4时,1634满足条件,因为 1^4 + 6^4 + 3^4 + 4^4 = 1634。
当N=5时,92727满足条件。
实际上,对N的每个取值,可能有多个数字满足条件。
程序的任务是:求N=21时,所有满足条件的花朵数。注意:这个整数有21位,它的各个位数字的21次方之和正好等于这个数本身。
如果满足条件的数字不只有一个,请从小到大输出所有符合条件的数字,每个数字占一行。因为这个数字很大,请注意解法时间上的可行性。
这次我重新修改一下这篇文章,之前的内容在下面,一开始,我是直接按照大数运算的方法,从10000......到99999........暴力枚举,虽然程序没有出结果,但是我将正确的答案带进去发现计算模块和验证模块是正确的,于是就心满意足了,也没有验证一下计算出结果到底需要多久,今天看了一个教学视频,恍然大悟,回想一下以前的分析算法复杂度,CPU的运算速度为10^9次/秒,而要计算的数超过10^21个,就算纯粹的枚举数字,都组要10^12秒,换算下来接近31709年。。。在我有生之年是看不到结果了。。。所以这次换了一个方法,个人感觉很巧妙,原代码是java版的,还好鄙人学了一个学期的java,研究了一下,下面说说分析过程。
首先,我们任然使用之前的大数系统,也就是通过字符串来表示大数。因为题目要求是说每一位的21次方之和,也就是说,这个和实际上跟数字的顺序并没有关系,举个简单的例子,123和321的每一位的3次方之和并没有区别,然后我们就可以换一个角度来考虑这个问题,如果纯粹的枚举数字,必定会出现大量的重复,为了避免重复,我们直接枚举每一个数字出现的次数,比如0出现3次,1出现2次,2出现4次。。。如果能很好的处理这个排列组合,使之不发生重复,那效率提高了不是一点点。
关键问题是怎样模拟这个排列呢,总不能用10个for循环吧,那种太low了,在循环太多的情况下,我们用什么方法呢?没错,就是递归。
在递归的时候,我们要枚举从0到9这10个数每一个数出现的次数,递归出口当然就是枚举到9时,因为总位数为21位,只要前9个数出现的次数确定了,那第十个数(9)出现的次数也就确定了,就是21-used,当获得了一组数据nn[]之后,我们将这组数据所代表的和sum[]求出来,这里注意,因为枚举的是每一个数字出现的次数,所以我们并不知道那个21位数到底是几,但是,我们可以通过nn[]这组数据,把一个21位的空数组num[]填满,不用考虑顺序,填满之后,我们把sum和num进行排序,如果这两个数组的每一位都相等,那说明这个sum的值就是我们要求的值,输出即可。
这个代码在我i7的本子上运行80+s,还算可以了
#include<iostream>
#include<algorithm>
#include<memory.h>
#include<cstring>
using namespace std;
int square[10][21];
int num[21]={0};
int sum[21]={0};
void carry(int *t)
{
int i;
for(i=19;i>=0;i--)
{
t[i]=t[i]+t[i+1]/10;
t[i+1]=t[i+1]%10;
}
}
void add(int *a,int *b)
{
int i;
for(i=0;i<=20;i++)
{
a[i]=a[i]+b[i];
}
carry(a);
}
void mul(int *a,int b)
{
int i;
for(i=0;i<21;i++)
a[i]=a[i]*b;
carry(a);
}
void showNum(int *n)
{
int i;
for(i=0;i<21;i++)
cout<<n[i];
}
void init()
{
int i,j;
for(i=0;i<10;i++)
{
for(j=0;j<20;j++)
{
square[i][j]=0;
}
square[i][20]=i;
}
for(i=0;i<10;i++)
{
for(j=0;j<20;j++)
{
mul(square[i],i);
}
}
}
int judge()
{
int i;
int temp[21];
for(i=0;i<21;i++)
temp[i]=sum[i];
sort(temp,temp+21);
sort(num,num+21);
for(i=0;i<21;i++)
{
if(num[i]!=temp[i])
return 0;
}
return 1;
}
void getSum(int *nn)
{
int i,j,p;
p=0;
for(i=0;i<10;i++)
{
for(j=0;j<nn[i];j++)
{
add(sum,square[i]);
num[p++]=i;
}
}
if(sum[0]<10&&judge())
{
if(sum[0]>0)
{
showNum(sum);
cout<<endl;
}
}
memset(sum,0,sizeof(sum));
}
void dfs(int *nn,int pos,int used)
{
if(pos==9)
{
nn[9]=21-used;
getSum(nn);
return;
}
for(int i=0;i<=21-used;i++)
{
nn[pos]=i;
dfs(nn,pos+1,used+i);
}
}
int main()
{
int i,j;
int nn[10]={0};
init();
dfs(nn,0,0);
return 0;
}
下面的方法适合在银河那样的超级计算机上运行,土豪专用。。。
没有想到什么好的解题方法,就是暴力枚举,不过枚举这么多数花的时间确实不少,关键是21位数的运算没有什么好用的数据类型,只能按照大数来处理,既然是大数,当然需要要用字符串来表示,这无疑更加降低了运算速度,然后要预处理一下,就是把0-9的21次方都事先算出来,不过这个高精度运算同样要按照大数原则运算,否则位数太长,然后很不情愿的又要编一个大数乘法函数,函数中所有的数都应该按照21位处理,虽然浪费点内存,但代码清晰,最后就是把每一位数的21次方相加,看得到的和是否跟那个数一样,因此还需要一个判断函数,就这样,我写出了程序,但很明显需要优化,因为运算出结果所花的时间太长了,不过我验证了结果,是正确的,希望大牛能想出更加高效的算法。
#include<iostream> #include<memory.h> using namespace std; int square[10][21]; int num[21]={0}; int sum[21]={0}; void carry(int *t) { int i; for(i=19;i>=0;i--) { t[i]=t[i]+t[i+1]/10; t[i+1]=t[i+1]%10; } } void add(int *a,int *b) { int i; for(i=0;i<=20;i++) { a[i]=a[i]+b[i]; } carry(a); } void mul(int *a,int b) { int i; for(i=0;i<21;i++) a[i]=a[i]*b; carry(a); } void showNum(int *n) { int i; for(i=0;i<21;i++) cout<<n[i]; } void init() { int i,j; for(i=0;i<10;i++) { for(j=0;j<20;j++) { square[i][j]=0; } square[i][20]=i; } for(i=0;i<10;i++) { for(j=0;j<20;j++) { mul(square[i],i); } } } void getSum() { int i; for(i=0;i<21;i++) { add(sum,square[num[i]]); } } int judge() { int i; for(i=0;i<21;i++) { if(sum[i]!=num[i]) return 0; } return 1; } int main() { int i,j; init(); /*for(i=0;i<10;i++) { for(j=0;j<21;j++) { cout<<square[i][j]; } cout<<endl; }*/ int temp[21]={0}; temp[20]=1; num[0]=1; for(i=0;num[0]!=2;i++) { add(num,temp); getSum(); if(judge()) { showNum(num); cout<<endl; } memset(sum,0,sizeof(sum)); } return 0; }
相关文章推荐
- 2013第四届 蓝桥杯c/c++B组预赛 解题报告(还在更新中。。。。。)
- 第六届蓝桥杯试题--生命之树 解题报告
- 第三届蓝桥杯C++本科B组决赛解题报告(更新中)
- 2013蓝桥杯预赛C/C++本科B组解题报告
- 第二届蓝桥杯C++本科B组初赛解题报告
- 第五届蓝桥杯省赛解题报告--神奇算式
- 蓝桥杯 买不到的数目 解题报告
- 第六届蓝桥杯省赛试题--垒骰子 解题报告
- 第六届蓝桥杯校园选拔赛试题---派遣敢死队 解题报告
- 第二届蓝桥杯C++本科B组决赛解题报告
- 2014 第五届蓝桥杯预赛c/c++本科B组 解题报告
- 2014蓝桥杯本科C/C++组预赛第9题<斐波那契>解题报告
- 蓝桥杯 历届试题 买不到的数目 解题报告(完全背包,数论)
- 串中取三个不同字符 蓝桥杯 解题报告
- 2012“蓝桥杯”全国软件专业人才设计与创业大赛解题报告
- 2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告
- 第四届 蓝桥杯 第三题 第39阶台阶 搜索 解题报告
- 解题报告 之 2015蓝桥杯 垒骰子
- 蓝桥杯 砝码称重 递归 解题报告
- 第五届蓝桥杯软件大赛C/C++本科B组解题报告