2 条题解

  • 0
    @ 2025-11-27 12:04:41
    #include <bits/stdc++.h>
    using namespace std;
    
    int main(){
        int n;
        cin>>n;
        //思路:根据位值原理,题目意思是原数的二进制最右位不能是1,然后输出位为1的数位
        if(n%2!=0){
            cout<<"-1"<<endl;
            return 0;
        }
        //是偶数
        int nowbit=2;//当前循环位值
        n=n/2;
        vector<int> ans=vector<int>();//用动态数组vector做容器,方便逆序输出
        while(n>0){
            if(n%2==1){//二进制里取末位的方法是mod2
                ans.push_back(nowbit);
            }
            n=n/2;
            nowbit=nowbit*2;
        }
        for(int i=ans.size()-1;i>=0;i--){
            cout<<ans[i];
            if(i!=0){//最后一个数字不要输出空格
                cout<<" ";
            }
        }
    }
    
    • 0
      @ 2025-11-27 12:03:35

      P7071 这道题(CSP-J 2020 T1)考察的是对二进制位运算的理解。虽然题目包装了一个“优秀的拆分”的概念,但剥开外壳,本质上非常数学。

      下面我给你提供几个关键的突破口:

      1. 审题:什么叫“优秀的拆分”?

      题目有两个硬性条件:

      1. 拆分成 22 的正整数次幂
      2. 这些数必须 互不相同

      我们来分析一下:

      • 22 的正整数次幂:指的是 21,22,23,2^1, 2^2, 2^3, \dots,也就是 2,4,8,16,2, 4, 8, 16, \dots
      • 注意! 20=12^0 = 1 不是正整数次幂(0不是正整数)。这意味着拆分里不能包含 1
      • 互不相同:这让你想到了什么?对了,这就是二进制的定义!任何一个正整数都可以唯一地表示为二进制形式(即若干个不同的 2 的幂之和)。

      2. 第一步:判断无解情况 (-1)

      根据上面的分析,唯一的限制就是不能有 202^0(也就是 1)

      • 在二进制表示中,最低位(最右边一位)代表 202^0
      • 如果一个数的二进制最低位是 1,说明这个数包含 11,它就是一个奇数
      • 如果一个数的二进制最低位是 0,说明这个数不包含 11,它就是一个偶数

      结论

      • 如果是奇数,它一定包含 11,不满足“优秀的拆分”,直接输出 -1
      • 如果是偶数,它一定能被拆分为 2,4,8...2, 4, 8... 的和,一定有解。

      3. 第二步:如何拆分并从大到小输出?

      题目要求从大到小输出。这其实是提示我们要从高位向低位检查。

      对于 n107n \le 10^7,最大的 22 的幂次不会超过 2242^{24}(因为 $2^{10} \approx 1000, 2^{20} \approx 100W, 2^{24} \approx 1600W$)。

      解法一:贪心(数学思维) 你可以准备一个循环,从 22 的某个高次幂开始(比如 2242^{24}),试探能不能“装”进 nn 里。

      • 如果当前幂次 pnp \le n,说明 nn 里包含这个 pp。输出 pp,然后 nn 减去 pp
      • 继续试探下一个更小的幂次。

      解法二:位运算(更像程序员的思维) 直接检查 nn 的二进制每一位。

      • 使用一个循环,变量 i 从大到小(比如从 24 循环到 1)。
      • 检查 nn 的第 i 位是否为 1。
      • 如果是 1,说明这一位代表的数值是 2i2^i,输出它。
      • 提示:如何计算 2i2^i?可以用 1 << i。如何检查第 i 位?可以用 n & (1 << i)

      4. 代码框架提示

      #include <iostream>
      #include <cmath> // 或者用位运算代替 pow
      using namespace std;
      
      int main() {
          int n;
          cin >> n;
      
          // 1. 判断奇偶性
          if (n % 2 != 0) {
              cout << -1 << endl;
              return 0;
          }
      
          // 2. 从大到小枚举 2 的幂
          // 10^7 大约是 2 的 23-24 次方左右,所以从 24 开始向下枚举足够了
          // 注意题目要求正整数次幂,所以循环只到 1,不到 0
          for (int i = 24; i >= 1; i--) {
              // 计算 2 的 i 次方
              int power = (1 << i); // 这里的 << 是左移运算符,1 << i 等于 2^i
              
              // 3. 判断 n 是否包含这个幂
              // 方法A: 简单的减法逻辑
              if (n >= power) {
                  cout << power << " ";
                  n -= power;
              }
              
              // 方法B: 位运算逻辑
              // if ((n & power) == power) { ... }
          }
      
          return 0;
      }
      

      这道题其实就是让你手动实现一个“十进制转二进制”的过程,只是跳过了 202^0 这一位。只要思路通了,代码非常短!加油!

      • 1

      信息

      ID
      3824
      时间
      1000ms
      内存
      256MiB
      难度
      10
      标签
      递交数
      4
      已通过
      4
      上传者