题目大意:输入一个正整数n(1<=n<=10^9)以此构造一个正整数序列1,2,3,4,...,n,问最少几次操作(任意选取几个数,共同减去一个相同的正整数)可以将该序列归零。
思路1:对于一个单调递增的线段,我们对他进行几次操作可以让他回到x轴上
我们可以对线段的任意多条子线段或者一点进行上下位移(相同的距离)
不难发现将其对半然后移动,就可以变成两个一模一样的1/2线段,接下来的每一次操作 都相当于执行了两次操作。因为只能上下移动相同的距离所以不会出现接下来每次操作 能产生更高的效率。
于是我们以此类推就能将线段分成点,让这些点全部回归x轴了。
#include <bits/stdc++.h>
using namespace std;
int n;
inline int recur(int x){
if(x==1)return 1;
return recur(x/2)+1;//每次执行对半分的时候相当于进行了一次操作
}
int main(){
cin>>n;
cout<<recur(n);
return 0;
}
同时很明显,每次对新的数进行二分,即((n/2)/2)/2。相当于2^x=n,x+1为多少
如果2^x太大了那就直接输出x就可以了。
#include <bits/stdc++.h>
using namespace std;
int n;
int main(){
cin>>n;
for(int i=0;;i++)
if(pow(2,i)==n){
cout<<i+1;
break;
}else if(pow(2,i)>n){
cout<<i;
break;
}
return 0;
}
根据if和else if两者条件来看很明显是在求log2n,故代码可以进一步简洁。
#include <bits/stdc++.h>
using namespace std;
int n;
int main(){
cin>>n;
cout<<(int)log2(n)+1;
return 0;
}
思路2:将正整数序列全部变成二进制,每次操作让0的数量尽可能的变多
0001 0010 0011 0100 0101 0110 0111
每次直接去掉2^0~2^2,最多三次操作 比如0001 0011 0101 0111这四个数都有0001,直接去掉最后一位。可见没有比这种方法增加0的速度更快。
故答案直接输出最高位1所在的位置,如最高位1在第三位,则输出3,利用位运算可以快速算出答案。
并且最高位1只有可能出现在最大的数上面
故代码
int n,count=1,ans=-1;
cin>>n;
while(n){
if(n&1)ans=count;
n>>=1;
count++;
}
cout<<ans;
return 0;