ACM市竞赛题 关于递归算法设计

cyj78117520 2009-12-08 05:02:43
刚刚ACM市竞赛回来,比赛的时候有一道题目做了好久没搞定很是郁闷,发上来讨论讨论,求高手指点。
题目:
一个数字链由数字的每位平方相加而成。
例子:44->32->13->10->1->1 .4*4+4*4=32,3*3+2*2=13 .........
给你一个数字N计算出N以内的所有符合要求的数的个数(包括N)。1<=N<=5000000
输入包含一个T,表示测试次数。接着是一个整数N

输入样例:
4
1
10
1000
10000
输出样例:
1
3
143
1442

我的代码(错误的- -):
#include <iostream>
#include <vector>
using namespace std;

vector <int> cyj; // 保存符合要求的数字。
vector <int> tt; // 临时数组。

int a[5000001]; // 用于标记符合要求的数字1表示是,0表示不是。
void judge(int n)
{
tt.insert(tt.begin(),n); //先将n插入tt数组。
if(n==1||a[n]==1) // 如果n==1或者n为已经标记的数字则执行一下语句。
{
for(int j=0;j<tt.size();j++) // 遍历tt数组
{
if(a[tt[j]]==0) // 如果tt[j]的值未被标记则
{ // 标记该数字,并且将值插入cyj数组。
a[tt[j]]=1;
cyj.insert(cyj.begin(),tt[j]);
}
}
tt.clear(); // 清空tt数组,返回。
return ;
}
int m=n; // 用m 保留n的原值
int sum=0;
while(n>0) // 将n每位数字提取并把平方的值与sum相加。
{
int t=n/10;
sum+=t*t;
n/=10;
}
if(sum>m) // 如果sum大于n原来的值则该数不符合要求直接返回。
{
return ;
tt.clear();
}
else // 否则
{
judge(sum);
}
if(sum<10&&sum!=1) // 如果sum小于10且不等于1则返回。
{
tt.clear();
return ;
}
}

int main()
{
int T;
cin>>T;
while(T--) // T为测试次数
{
memset(a,0,sizeof(a)); // a数组初始化为0
int n;
cin>>n;
if(n==1) //1,和小于10的情况单独考虑。
{
cout<<'1'<<endl;
continue;
}
else if(n<10)
{
cout<<'2'<<endl;
continue;
}
a[1]=1; // 1,10是符合要求的数字 。
a[10]=1;
cyj.insert(cyj.begin(),1); // 因为题目规定有两个1所以cyj里面插入两个1.
cyj.insert(cyj.begin(),1);
for(int i=10;i<=n;i++)
{
if(a[i]==1) continue; // 如果i是已经标记的数则继续循环。
judge(i);
}
cout<<cyj.size()<<endl;
}
return 0;
}
...全文
300 16 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
lxhsjl 2010-09-27
  • 打赏
  • 举报
回复
觉得题目写得不清楚
honghu069 2009-12-09
  • 打赏
  • 举报
回复

if(n==1) //1,和小于10的情况单独考虑。
{
cout<<'1'<<endl;
continue;
}
else if(n<10)
{
cout<<'2'<<endl;
continue;
}


如果n=2,应该输出啥呢
如果手算没有算错的话,2就陷入死循环了
2 4 16 37 58 89 145 42 20 4
其实就是开个数组做标记,看算过的数有没有出现过,有出现就会陷入死循环
而且这个数组也不用很大
  • 打赏
  • 举报
回复
没看懂
cyj78117520 2009-12-09
  • 打赏
  • 举报
回复
同样感谢11楼的朋友
cyj78117520 2009-12-09
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 baihacker 的回复:]
有的数...能变成1,而有的数会陷入
4
16
37
58
89
145
42
20
的死循环
(1亿之内已验证,未证明)
因为可以事先打表
用ans[i]表示N为i的时候的答案
然后验证一下i+1,如果i+1能变成1,就是ans[i+1] = ans[i] + 1,否则ans[i+1] = ans[i]

[/Quote]

这位真是高手,一看就看破题目。题目是应该这样解的。我的思路让我陷入了困难。。。
misterliwei 2009-12-09
  • 打赏
  • 举报
回复
优化了一下:

//ACM.C
#include "stdio.h"

int N = 10000; //求10000范围内的符合条件的个数。
int array[550];

//下面这个函数返回一个指定数据的位平方和。
int square(int num)
{
int n;
n = 0;
while(num != 0)
{
n = n + (num % 10) * (num % 10);
num /= 10;
}

return n;
}

//递归函数
int judge(int num, int i)
{
int ret;
// printf("%d %d\n", num, i);

num = square(num);

if (num == 1)
return 1;

if (num > 550)
return 0;

if (array[num] == 1)
return 1;
else if (array [num] != 0)
return 0;

array[num] = i;
ret = judge(num, i);
if ( ret == 1)
array [num] = 1;

return ret;
}

void main()
{
int count, i;

//清除标志
for (i = 0; i < 550; i ++)
array[i] = 0;

//循环N次。
for (i = 1, count = 0; i <= N; i++)
{
//对每个数据进行判断是否符合条件
if (judge(i, i) == 1)
{
count ++; //符合条件者,计数器加1.

// printf("%d is ok\n", i);
}
// else
// printf("%d is not ok!\n", i);
}

printf("count= %d\n", count);
return;
}
misterliwei 2009-12-09
  • 打赏
  • 举报
回复

//ACM.C
#include "stdio.h"

int N = 10000; //求10000范围内的符合条件的个数。
int array[550];

//下面这个函数返回一个指定数据的位平方和。
int square(int num)
{
int n;
n = 0;
while(num != 0)
{
n = n + (num % 10) * (num % 10);
num /= 10;
}

return n;
}

//递归函数
int judge(int num, int i)
{
// printf("%d %d\n", num, i);

num = square(num);

if (num == 1)
return 1;

if (num > 550)
return 0;

if (array[num] == 1)
return 1;
else if (array [num] != 0)
return 0;

array[num] = i;
return judge(num, i);
}

void main()
{
int count, i, j;

//清除标志
for (i = 0; i < 550; i ++)
array[i] = 0;

//循环N次。
for (i = 1, count = 0; i <= N; i++)
{
//对每个数据进行判断是否符合条件
if (judge(i, i) == 1)
{
count ++; //符合条件者,计数器加1.

//将其涉及的所有标志置1,以供后来的数据使用。
for(j = 0; j < 550; j ++)
{
if (array[j] == i)
array[j] = 1;
}
// printf("%d is ok\n", i);
}
// else
// printf("%d is not ok!\n", i);
}

printf("count= %d\n", count);
return;
}
zhangxfeng112 2009-12-08
  • 打赏
  • 举报
回复
再优化的方法就是找数字之间的规律,例如:
1) 123,321, 213,321.....只判断一个就可以代表全部。
2) 由勾股定理,342,与52等价。。。烦34成对出现的直接变成5.
3) 数字中有0的,直接去掉。。。
4) 由于1满足要求,所以10,100,1000,10000满足要求。。。
...................
分段打印一下,把小于1000的都打印出来,观察观察,再找找规律。
zhangxfeng112 2009-12-08
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 deng1243 的回复:]
满足的要求就是最后必须是1吗?
[/Quote]
如果要是这个要求的话,我的建议是:
建一个真值表,把100以内,满足这样条件的数都记下来。
于是数字链差不多3,5项就会变成一个两位数,马上查表就可以判断出它是否符合要求。

第二种方案,是受6楼的启发得来,把所有满足要求的数都保存下来,伪代码如下:
for(i = 1; i<= N; i++)
{
int k = i的各位数之和;
while (k > i) k = k的各位数之和;
在保存的结果中查找k;
if(存在)
i满足要求,把i插入到结果集中
else
i不满足要求,跳过。
}
printf("%d",结果集中元素的个数);
zhangxfeng112 2009-12-08
  • 打赏
  • 举报
回复
没看懂LZ的描述。
数字链看明白了,
可是楼主说的满足要求,是满足什么要求啊????
baihacker 2009-12-08
  • 打赏
  • 举报
回复
有的数...能变成1,而有的数会陷入
4
16
37
58
89
145
42
20
的死循环
(1亿之内已验证,未证明)
因为可以事先打表
用ans[i]表示N为i的时候的答案
然后验证一下i+1,如果i+1能变成1,就是ans[i+1] = ans[i] + 1,否则ans[i+1] = ans[i]
deng1243 2009-12-08
  • 打赏
  • 举报
回复
满足的要求就是最后必须是1吗?
Victor_Dinho 2009-12-08
  • 打赏
  • 举报
回复
满足什么要求啊?
M_S_D_N 2009-12-08
  • 打赏
  • 举报
回复
同2楼,也没看明白。。。。
honghu069 2009-12-08
  • 打赏
  • 举报
回复
题目没看懂

符合要求的个数

要求是啥.....
cyj78117520 2009-12-08
  • 打赏
  • 举报
回复
up

70,020

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧