1. 程式人生 > >Codeforces Round #529 (Div. 3) C. Powers Of Two(數學????)

Codeforces Round #529 (Div. 3) C. Powers Of Two(數學????)

傳送門

 

題意:

  給出一個整數 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 }
View Code

  其實,在比賽時,並沒有做出這道題,不過也有點小想法,還不成熟,賽後看排名,無意間看到了hdu大神Claris的排名,然後,看了一下Claris的提交程式碼,哇,真簡潔,

  是我目前無法達到的。

  大約花費了一個多小時的時間才理解了%%%%%%%%%%%%