Codeforces Round #529 (Div. 3) C. Powers Of Two(数学????)

 

传送门

 

•题意

  给出一个整数 n ,问能否将 n 分解成 k 个数之和,且这 k 个数必须是 2 的幂。

  如果可以,输出"YES",并打印出任意一组解,反之输出"NO";

•题解

  预备知识补充:如何求出数 num 最少需要多少个 2的幂之和?

  例如 :

    num = 3 = 20+21至少需要两个

    num = 4 = 22 至少需要一个

    num = 17 = 24+20 至少需要两个

  根据贪心的思想 :

    令 2x ≤ num,求出最大的 x ,那么此时num可以表示为 num = 2x+num1 ( num1 = num-2x );

    num1接着重复上述过程,求出  ≤num1 的最近的2x1,num1 = 2x1+num2 ( num2 = num1-2x1 );

    那么num最少的2的幂之和就为 : 2x+2x1+2x2+.......;

  如何求出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(num/(2^i)为奇数)
3         那么2^i就为num最少需要的2的幂之和的成员之一

  并且,$2^i$ 等价于 两个 $2^{i-1}$,所以,可以通过 $2^i$ 转化为 $2^{i-1}$ 开填充;

•Code

 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 }
View Code

•感悟  

  其实,在比赛时,并没有做出这道题,不过也有点小想法,还不成熟;

  赛后看排名,无意间看到了hdu大神Claris的排名,然后,看了一下Claris的提交代码,哇,真简洁,

  是我目前无法达到的。

  大约花费了一个多小时的时间才理解了%%%%%%%%%%%%

 


分割线2019.5.23

重新温习了一下这道题;

假设 n 最少由 k 个2的幂组成:

n = 2x1 + 2x2 +.......+ 2xk

那么 n / 2xi 为奇数;

今天重新想了一下这个,没有像之前那么繁琐的推公式,一想就想到;

如果 n / 2xi 为偶数,那么 2xi 可以变为 2xi+1 使得组成 n 这个幂值更大,那么,肯定比2xi所需的2的幂少,与假设矛盾;

 


再次分割2019.10.24

  二进制思想;

  十进制数 n 对应的二进制的第 i 位如果为 1,那么 $2^i$ 就是二进制转十进制 n 的组成部分;

  那么,也即是说,n 对应的二进制有多少个 1,n 就至少需要多少个 2 的幂之和;

  如果这些不够 k 个,那么就通过一个 $2^i$ 可以转化为两个 $2^{i-1}$ 的形式来增加幂之和的个数; 

 

posted @ 2018-12-29 11:21  HHHyacinth  阅读(228)  评论(0编辑  收藏  举报