思路
刚拿到题,想了一些方法但都被推翻了,在这里列举出来,并给出反例:
-
每次减去最小的因数,反例: 1024 1024 1024 等形如 a k a^k ak 的数,每次都会减去 a a a 导致 a a a 的出现次数超过 2 2 2 次。
-
每次减去大于等于 x \sqrt x x 的因子, x x x 为目前的数,并特判指数的情况,反例: 35 35 35 等由两个质数相乘的数,会减去多次较大的质数,导致出现次数超 2 2 2 次。
-
记忆化搜索,没反例,但是时间复杂度会超。
在经过多次尝试后,我终于想到了一点,如果 x x x 是形如 2 k 2^k 2k 的数,可以每次减去 x 2 \frac x 2 2x 直到变为 1 1 1。
那么,我们尝试把给定的数变为 2 k 2^k 2k 的形式。
发现给定的数离 2 k 2^k 2k 的差距就只是二进制下除了最高位的 1 1 1。
所以可以先每次减去 lowbit ( x ) \operatorname{lowbit}(x) lowbit(x),直到 x x x 为 2 k 2^k 2k,然后再每次减去 x 2 \frac x 2 2x。
发现前半部分最多减去了一次 2 l 2^l 2l 其中 0 ≤ l < k 0\le l <k 0≤l<k。
然后后半部分会减去 2 m 2^m 2m 其中 0 ≤ m < k 0\le m <k 0≤m<k 各一次。
所以不会超过 2 2 2 次的限制。
AC code
#include<bits/stdc++.h>
using namespace std;
inline int lowbit(int x){return x&(-x);}
int T,n,ans[1005],cnt,nn;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n),cnt=0,nn=n;
while(n&(n-1))//判断是否变为2^k
{
ans[++cnt]=lowbit(n),n-=lowbit(n);//记录操作
}
while(n>1) ans[++cnt]=n/2,n/=2;//每次减去n/2
printf("%d\n",cnt+1);//操作cnt次,共cnt+1个数
for(int i=1;i<=cnt;++i)
{
printf("%d ",nn),nn-=ans[i];
}
printf("%d\n",nn);
}
return 0;
}