P7071 [CSP-J2020] 优秀的拆分

记录29

#include <bits/stdc++.h>
using namespace std;
int main(){
	int n,a[35]={};
	cin>>n;
	if(n%2==1){
		cout<<"-1";
		return 0;
	}
	int cnt=0;
	while(n!=0){
		if(n%2==1) a[cnt]=1;
        cnt++;
		n/=2;
	}
	for(int i=cnt-1;i>=0;i--){
		if(a[i]==1) cout<<fixed<<setprecision(0)<<pow(2,i)<<" ";
	}
    return 0;
}

突破点

对于正整数 n 的一种特定拆分,我们称它为“优秀的”,当且仅当在这种拆分下,n 被分解为了若干个不同的 2 的正整数次幂。注意,一个数 x 能被表示成 2 的正整数次幂,当且仅当 x 能通过正整数个 2 相乘在一起得到。👉一个数拆成不同2的次方之和

从大到小输出这个拆分中的每一个数,若不存在优秀的拆分,输出 -1

👉偶数由大到小输出拆出来的数,奇数输出-1


思路

  1. 奇数输出-1
  2. 偶数拆成2的次方形式👉转换成二进制
  3. 由大到小输出拆出来的数

代码简析

#include <bits/stdc++.h>
using namespace std;
int main(){
	int n,a[35]={};
	cin>>n;
	if(n%2==1){
		cout<<"-1";
		return 0;
	}
	...
    return 0;
}

n为输入数

a[35]是用来保存2进制的数组

if条件判断是奇数直接输出-1

:int类型32位

#include <bits/stdc++.h>
using namespace std;
int main(){
	int n,a[35]={};
	cin>>n;
	if(n%2==1){...}
	int cnt=0;
	while(n!=0){
		if(n%2==1) a[cnt]=1;
        cnt++;
		n/=2;
	}
	...
    return 0;
}

cnt代表填进去的序号

循环有点类似数位分离,每次分离出2的进位

a数组来存储每一位

#include <bits/stdc++.h>
using namespace std;
int main(){
	int n,a[35]={};
	cin>>n;
	if(n%2==1){
		cout<<"-1";
		return 0;
	}
	int cnt=0;
	while(n!=0){
		if(n%2==1) a[cnt]=1;
        cnt++;
		n/=2;
	}
	for(int i=cnt-1;i>=0;i--){
		if(a[i]==1) cout<<fixed<<setprecision(0)<<pow(2,i)<<" ";
	}
    return 0;
}

for循环输出每一位

pow()函数结果非常大或者非常小会输出科学计数法

cout<<fixed<<setprecision(0)设置小数点后的精度为0,即不显示小数部分。


补充

在C++中,pow 函数用于计算一个数的幂,其返回值类型为 double。当计算结果非常大或非常小时,pow 函数的输出可能会以科学计数法(带 e)的形式显示。例如,pow(10, 9) 的结果是 1e+09,而不是 1000000000

为了避免这种情况,可以使用以下方法将结果格式化为普通数字形式。

1. 使用 std::fixed 和 std::setprecision

std::fixedstd::setprecision 是 C++ 标准库中的 I/O 操纵符,用于控制浮点数的输出格式。

语法

#include <iomanip> // 包含 std::fixed 和 std::setprecision
  • std::fixed:设置浮点数为固定小数点表示法。

  • std::setprecision(n):设置浮点数的小数点后保留的位数为 n

2. 示例代码

示例1:避免科学计数法

#include <iostream>
#include <cmath>
#include <iomanip> // 包含 std::fixed 和 std::setprecision
using namespace std;

int main() {
    double result = pow(10, 9); // 计算 10 的 9 次幂
    cout << fixed << setprecision(0) << result << endl; // 设置为固定小数点表示法,小数点后保留 0 位
    return 0;
}
输出结果
1000000000

3. 详细解释

3.1 std::fixed
  • 作用:设置浮点数为固定小数点表示法,而不是科学计数法。

  • 用法:在输出流中使用 std::fixed,例如 cout << fixed << value;

3.2 std::setprecision(n)
  • 作用:设置浮点数的小数点后保留的位数为 n

  • 用法:在输出流中使用 std::setprecision(n),例如 cout << setprecision(0) << value;

4. 注意事项

4.1 精度问题
  • setprecision(0):设置小数点后保留 0 位,适用于整数结果。

  • setprecision(n):设置小数点后保留 n 位,适用于浮点数结果。

4.2 临时设置
  • std::fixedstd::setprecision 的设置是临时的,只影响当前输出流。如果需要多次使用,可以在每次输出前重新设置。

5. 总结

  • 问题pow 函数的输出可能会以科学计数法(带 e)的形式显示。

  • 解决方案:使用 std::fixedstd::setprecision 控制浮点数的输出格式。

  • 示例

    cout << fixed << setprecision(0) << pow(10, 9) << endl;
  • 作用

    • std::fixed:设置为固定小数点表示法。

    • std::setprecision(n):设置小数点后保留的位数为 n

### CSP-J2020 优秀拆分策略与解析 #### 题目背景 CSP-J2020 的“优秀拆分”问题要求将一个整数 $ n $ 拆分为若干个不同的 $ 2^k $(其中 $ k \geq 1 $)的和。如果 $ n $ 是奇数,则无法满足条件,输出 -1;否则需要输出具体的拆分结果。 #### 解题思路 1. **奇偶性判断**:根据题目规则,如果 $ n $ 是奇数,则直接输出 -1,因为奇数无法被表示为若干个 $ 2^k $ 的和[^1]。 2. **二进制分解**:对于偶数 $ n $,可以将其转化为二进制形式。每个二进制位上的 1 表示该位置对应的 $ 2^k $ 存在于拆分中[^3]。 3. **次幂限制**:由于 $ n \leq 10^7 $,而 $ 2^{25} > 10^7 $,因此只需要考虑 $ k $ 从 1 到 24 的范围。 4. **从高到低遍历**:从最大的 $ 2^k $ 开始逐个检查是否能被 $ n $ 整除。如果可以,则将该 $ 2^k $ 加入拆分结果,并更新 $ n = n - 2^k $[^5]。 5. **输出结果**:最终输出所有参与拆分的 $ 2^k $ 值。 #### 示例代码实现 以下是一个完整的 C++ 实现: ```cpp #include <iostream> #include <cmath> using namespace std; int main() { long long n; cin >> n; if (n % 2 == 1) { // 如果是奇数 cout << "-1"; return 0; } for (int i = 24; i >= 1; i--) { // 从大到小遍历2的次幂 if (n >= (1LL << i)) { // 检查是否能减去当前的2^i cout << (1LL << i) << " "; // 输出当前的2^i n -= (1LL << i); // 更新剩余值 } } return 0; } ``` #### 注意事项 - 使用 `1LL << i` 而不是 `pow(2, i)`,避免浮点数精度问题[^5]。 - 确保 $ k \geq 1 $,因为题目明确要求不包含 $ 2^0 $[^1]。 - 输出时注意格式,多个数字之间用空格分隔[^3]。 #### 示例分析 - 输入 $ n = 6 $: - $ 6 $ 是偶数,可以拆分为 $ 2^2 + 2^1 $。 - 输出为 `4 2`。 - 输入 $ n = 7 $: - $ 7 $ 是奇数,无法满足条件。 - 输出为 `-1`[^1]。 #### 时间复杂度 由于只需要遍历 $ k $ 从 1 到 24,时间复杂度为 $ O(\log n) $,能够高效处理 $ n \leq 10^7 $ 的数据范围。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值