一段序列全部变成0的问题

本文介绍了一道编程题目,涉及如何通过最少次数的操作将1到N的序列变成全0。关键在于每次操作时让序列中尽可能多的数变得相同,通过将序列分为两半并逐步减去适当数值实现。解题方法是计算n向下取2的幂次方的次数加1。
摘要由CSDN通过智能技术生成

这是一道编程思维题,也是某大厂的笔试题,但也有可能在新生(比如我)的OJ中出现,因此就专门写了这样一段总结。

题目描述

现有一段序列1、2、...、N,你需要进行一种操作,是它们全都变成0

这种操作是:选取这段序列中的一些数字,将它们减去L

现在请问进行操作的最少次数是多少?

如序列1、2、3,可以先选择1、2、3减去1变为0、1、2,再选择1、2减去1变为0、0、1,最后选择1减去1变为0、0、0,这种方法需要3次操作; 而如果先选择2、3减去2变为1、0、1,再选择1、1减去1变为0、0、0,只需要2次操作

输入

测试数据有多组

输入一个整数N(1 <= N <= 1e9)

输出

对于每组测试数据

输出最少次数

样例

输入                                      输出

1                                            1

2                                            2 

3                                            2

这道题首先要理解:输入一个数字,为什么变成一个序列?

其实不需要担心太多,数字是多大,这个序列就是1到这个数的所有整数集合

比如输入3代表123,输入7代表1234567等等。

那么这个问题怎么解呢?

我们希望减去一个数后,序列都有尽可能多的数变成0,也就是说,每一次相减前,我们需要让这个序列拥有尽可能多的数是相同的。

那么一个连续的,n个不同的数组成的序列,将其中的一部分抽出来减去一个数后,最多会将不同的数减少到多少呢。

答案是n=n/2-1(n为奇数),n=n/2(n为偶数)

这种变法这么好,该怎么变呢,答案是将序列分成两半,后面一半减去固定的数之后每一个都等于前面一半的数。

比如1234567

我们分成123 4567两半,

第一次:

4567想和前面一样,就需要-4

序列变成1230123,

第二次:

接着看看前后的123,如何变成0?

我们需要把他们都提出来,然后123拆成1 23,

23部分-2,变成01

也就是变成了1010101,

第三次:

然后我们把1的部分提出来-1即可,

所以输入7,次数是3。

又例如123456

第一次拆成123 456,后部分-3变123123

第二次对于两相同子序列拆成1 23,后部分-2得到101101

第三次,抽出为1的-1即可。

所以输入6,输出也是3。

不难发现,我们每次都将一半的序列拿来操作,计算次数实际上就是n向下整除几次能得到0的次数,更直白点,就是取比n小的2的m次方那个m再加1。

2^m<n<2^(m+1),次数=m+1,

例如2^2<7<2^3,那么输入7时输出就是3。

这里代码采用不断缩小n的范围法。

*因为最大上限是10e9因此输入num要设置long型

#include <stdio.h>

int main() {
	long num;
	while (scanf("%ld", &num) == 1) {
		int sum = 0;
		while (num > 0) {
			sum++;
			num = num / 2;
		}
		printf("%d\n", sum);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shiroha Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值