[每日一题] 2021.09.08 luogu P5727 冰雹猜想

题目链接:P5727 【深基5.例3】冰雹猜想 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述:给出一个正整数 n(n≤100),然后对这个数字一直进行下面的操作:如果这个数字是奇数,那么将其乘 3 再加 1,否则除以 2。经过若干次循环后,最终都会回到 1。经过验证很大的数字(7×10^11)都可以按照这样的方式比变成 1,所以被称为“冰雹猜想”。例如当 n 是 20,变化的过程是 [20, 10, 5, 16, 8, 4, 2, 1]。

输入格式

输出格式

输入输出样例

输入

20

输出

1 2 4 8 16 5 10 20

解题思路及过程

这题主要考察数组的存放及输出,涉及到一些基础循环判断的问题,在洛谷看到这题感觉不难,就当练练手,读了读题目立马就有了思路,于是就赶紧去敲了代码。

第一次写的代码:

#include<iostream>
using namespace std;

int main()
{
	int n,a[100],c=0,i;
	cin>>n;
	a[c++]=n;
	while(1)
	{
	    if(n%2!=0)
	    {
        n=n*3+1;
	    a[c++]=n;
	    }
	    else
	    {
	    n=n/2;
	    a[c++]=n;
	    }
	    if(n==1)
	    break;
	}
	for(i=c-1;i>=0;i--)
	cout<<a[i]<<' ';
	cout<<endl;
	return 0;
}

第一次在编译器运行时发现数组少放了输入的数n。所以更改多加了这条语句(即数组第一个位置a[0]存放n):

a[c++]=n;

然后去OJ平台提交了代码:

第一次提交 60 分,两个测试点出现了错误。果然,读完题去敲代码立马提交大概率不能完全通过,会有一些测试点通过不了,一定会忽视一些问题。所以要多读几遍题目,仔细审题,找到题目当中的一些细节(例如数据范围、输出格式等)。

于是我就看了几遍题目,想了想。然后我在想是不是数组开的太小了,所以把数组改成a[100000];然后又去提交了一下:

第二次提交 80 分,果然是因为数组开小了。现在只有一个测试点出现了错误。然后看了看我的代码,我想想是不是可能是 while(1) 的问题,然后我把循环改成 do...while 后,代码如下:

#include<iostream>
using namespace std;
int main()
{
	int n,a[100000],c=0,i;
	cin>>n;
	a[c++]=n;
    do
    {
        if(n%2!=0)
        {
		n=n*3+1;
		a[c++]=n;
	    }
		else
		{
		n=n/2;
		a[c++]=n;
	    }
	}while(n!=1);
	for(i=c-1;i>=0;i--)
	cout<<a[i]<<' ';
	cout<<endl;
	return 0;
}

然后又去OJ提交了一下,测试结果如下:

第三个测试点仍然无法通过。

继续更换循环类型,这次用循环用 while 试试,条件判断为 n!=1while 部分语句如下:

while(n!=1)
{
    if(n%2!=0)
	{
    n=n*3+1;
    a[c++]=n;
	}
	else
    {
	n=n/2;
	a[c++]=n;
	}
}

 然后去提交代码,终于AC了!!!

这引发了我的思考:为什么用 while(n!=1) 可以,用 while(1) 或 do...while 语句就不行呢?

一开始我在想是不是 while(1) 可能是循环无法终止导致运行超时,然后就不断更改我的循环类型,但我的循环体里面有判断条件可以终止循环,所以就不可能是运行超时的问题。然后我发现while(1) 循环和 do...while 循环有一个共同点,那就是都是先执行循环体,再判断条件。这样的话,当我输入的n为1,那么先执行循环体,那也就是1→4→2→1,最后运行输出1 2 4 1。而当我用 while(n!=1) 时,先判断条件,再执行循环体。即当我输入n为1时,先判断条件,发现 n==1,不满足循环的条件 n!=1,因此不执行循环体,最后输出的结果为1。

然后我下载了测试点,发现确实是当输入为1时,输出为1,这说明我的想法正确的。再次回顾这道题目,可以发现这题应该更加严谨点,可以修改一下n的范围,例如将n的取值范围改为 (1<n<=100),或说明当n为1时不执行操作直接输出1,这样就可以避免歧义。不过这都不是重点,重要的是从错误中汲取经验与教训,下次再遇到类似问题时,可以知道从哪些方面去解决问题。

下面附上我的代码:

AC code 1:

使用 while(n!=1) 先判断条件,再执行循环体。

#include<iostream>
using namespace std;

int main()
{
	int n,a[100000],c=0,i;
	cin>>n;
	a[c++]=n;
    while(n!=1)
    {
        if(n%2!=0)
		{
		n=n*3+1;
		a[c++]=n;
	    }
		else
		{
		n=n/2;
		a[c++]=n;
	    }
	}
	for(i=c-1;i>=0;i--)
	cout<<a[i]<<' ';
	cout<<endl;
	return 0;
}

AC code 2:

或者可以修改一下第一次提交的代码,把 while(1) 循环体中的条件判断移到循环体的最前面,这样也可以实现先判断条件再执行循环体。

#include<iostream>
using namespace std;

int main()
{
	int n,a[100000],c=0,i;
	cin>>n;
	a[c++]=n;
	while(1)
	{
        if(n==1)
        break;
		if(n%2!=0)
		{
		n=n*3+1;
		a[c++]=n;
	    }
		else
		{
		n=n/2;
		a[c++]=n;
	    }
	}
	for(i=c-1;i>=0;i--)
	cout<<a[i]<<' ';
	cout<<endl;
	return 0;
}

AC code 3:

再者使用 do...while 语句也可以,这里需要在循环体开头加上判断终止条件语句。

#include<iostream>
using namespace std;

int main()
{
	int n,a[100000],c=0,i;
	cin>>n;
	a[c++]=n;
	do
	{
        if(n==1)
        break;
		if(n%2!=0)
		{
		n=n*3+1;
		a[c++]=n;
	    }
		else
		{
		n=n/2;
		a[c++]=n;
	    }
	}while(n!=1);
	for(i=c-1;i>=0;i--)
	cout<<a[i]<<' ';
	cout<<endl;
	return 0;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值