这是一道编程思维题,也是某大厂的笔试题,但也有可能在新生(比如我)的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;
}