题意:
给出一个整数 n ,问能否将 n 分解成 k 个数之和,且这 k 个数必须是2的幂。
如果可以,输出"YES",并打印出任意一组解,反之输出"NO";
题解:
预备知识补充:
如何求出 num 最少需要多少个2的幂之和?
例如 :
num = 3 <= 2^0+2^1至少需要两个
num = 3 <= 2^2 至少需要一个
num = 17 <= 2^4+2^0 至少需要两个
根据贪心的思想 :
令 2^x ≤ num,求出最大的 x ,那么此时num可以表示为 num = 2^x+num1 ( num1 = num-2^x );
num1接着重复上述过程,求出 ≤num1 的最近的2^x1,num1 = 2^x1+num2 ( num2 = num1-2^x1 );
那么num最少的2的幂之和就为 : 2^x+2^x1+2^x2+.......;
如何求出x,x1,x2,......呢?
2x : ≤ num 的距num最近的2的幂
2x1 : ≤ num1 的距num1最近的2的幂
2x2 : ≤ num2 的距num2最近的2的幂
2x3 : ≤ num3 的距num3最近的2的幂
易得 :
(1) : num / 2x = oddNum , num / 2x1 = oddNum , num / 2x2 = oddNum ,......
(2) : num / a = evenNum , num / b = evenNum , num / c = evenNum ,........
(1)证明 :
num / 2x = 1;
num1 / 2x1 = 1 → (num-2x) / 2x1 = 1 → num / 2x1 - 2x / 2x1 = num / 2x1 - 2x-x1 = 1 → num / 2x1 = 1 + 2x-x1 = oddNum ( 奇+偶 );
num2 / 2x2 = 1 → (num-2x-2x1) / 2x2 = 1 → num / 2x1 - 2x / 2x2- 2x1 / 2x2 = 1 → num / 2x1 = 1 + 2x-x2+2x1-x2 = oddNum ( 奇+偶+偶 );
.................
(2)证明 :
num / a = (num1+2x) / a = num1 / a + 2x / a = 0+偶 = evenNum;( 2x1 ≤ num1 < a )
..................
所以说:
1 for i:0 to k 2 if(n/2^i为奇数) 3 那么2^i就为num最少需要的2的幂之和的成员之一
其他细节,具体看代码吧!
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 int n,k; 6 int e[40];//e[i] : num需要e[i]个2^i 7 8 void Solve() 9 { 10 int curK=0; 11 for(int i=0;(1<<i) <= n;++i) 12 if(n>>i&1) 13 { 14 e[i]=1; 15 curK++; 16 } 17 //最少需要curK个2的幂 18 if(k < curK || k > n) 19 { 20 printf("NO\n"); 21 return ; 22 } 23 printf("YES\n"); 24 for(int i=30;~i;--i) 25 { 26 if(!e[i]) 27 continue; 28 if(curK == k) 29 break; 30 int x=min(e[i],k-curK); 31 e[i] -= x;//减少x个2^i 32 e[i-1] += 2*x;//增加2*x个2^(i-1) 33 curK += x;//比之前多了x个 34 } 35 for(int i=0;i <= 30;++i) 36 for(int j=0;j < e[i];++j) 37 printf("%d ",1<<i); 38 } 39 int main() 40 { 41 scanf("%d%d",&n,&k); 42 Solve(); 43 return 0; 44 }
其实,在比赛时,并没有做出这道题,不过也有点小想法,还不成熟,赛后看排名,无意间看到了hdu大神Claris的排名,然后,看了一下Claris的提交代码,哇,真简洁,
是我目前无法达到的。
大约花费了一个多小时的时间才理解了%%%%%%%%%%%%