目录
题目传送门
[CSP-J2020] 优秀的拆分 - 洛谷https://www.luogu.com.cn/problem/P7071
算法解析
做法
这题呢,其实可以直接枚举,但不知道哪个用,哪个不用,于是我们想到了用 DFS[深度优先搜索]来做这道题
递归参数
首先,一定要用一个变量来记录当前还剩多少和一个变量来记录当前要拆分出第几个数
另外,因为题目说了,不可以相同,所以还需要一个变量 pre 来记录上一次拆到了 2 的第几次幂,这次从 pre + 1 开始枚举
void dfs(int x, int t, int pre)
其中,x 代表还剩多少,t 代表当前要拆分出第几个数,pre 代表上一次拆到了 2 的第几次幂
递归边界
毋庸置疑,递归边界当然就是 x == 0
但是这里需要注意,有可能减多了,x 就变成了负数,就会进入死递归,所以递归时不能只是 else,需要 else if(x > 0) 才能继续枚举 2 的几次幂:
void dfs(int x, int t, int pre) {
if(x == 0) {
// 输出……
} else if(x > 0) {
// 继续枚举 2 的几次幂
}
}
递归
接下来,就要枚举减数 2 的几次幂了
怎么枚举呢,直接枚举指数就可以了
for(int i = pre + 1; pow(2, i) <= x; ++i)
然后,每次递归 x - pow(2, i) 就可以了
因为要拆分下一个了,所以要递归 t + 1
这时,需要传进去的 pre 就是 i 了,因为它下一个的上一个就是它本身
dfs(x - pow(2, i), t + 1, i);
这里用到了 pow(2, i),所以需要调用头文件
#include <math.h>
(pow(2, i) 是指 2 的 i 次幂)
输出
题目要求输出拆分过程,所以我们应该在递归的时候记录下来
所以要新添一个数组来记录拆分过程:
#define N 100000005
int a[N];
这时,递归时需要把 a[t] 赋成 pow(2, i):
for(int i = pre + 1; pow(2, i) <= x; ++i) {
a[t] = pow(2, i);
dfs(x - pow(2, i), t + 1, i);
}
这样,输出就很简单了:
for(int i = t - 1; i > 0; --i)
cout << a[i] << " ";
cout << endl;
为什么是从 t - 1 到 1 呢?
因为存的时候是从小到大存的,而要求从大到小输出,所以要从后往前输出,而当前要拆分第 t 个,但已经拆分完了,所以要从他的上一个开始
还有一个点需要注意:题目要求,如果没有优秀的拆分,则输出 -1,所以要有一个标记来记录当前有没有优秀的拆分:
bool f = 1;
所以当输出时,应该将 f 赋成 0:
for(int i = t - 1; i > 0; --i)
cout << a[i] << " ";
cout << endl;
f = 0;
最后,在主函数里判断有没有过优秀拆分就行了:
if(f)
cout << "-1\n";
题解
AC 代码
将上面的代码片段拼起来,就是最终的 AC 代码:
#include <iostream>
#include <math.h>
#define N 100000005
using namespace std;
int n, a[N];
bool f = 1;
void dfs(int x, int t, int pre) {
if(x == 0) {
for(int i = t - 1; i > 0; --i)
cout << a[i] << " ";
cout << endl;
f = 0;
} else if(x > 0) {
for(int i = pre + 1; pow(2, i) <= x; ++i) {
a[t] = pow(2, i);
dfs(x - pow(2, i), t + 1, i);
}
}
}
int main() {
cin >> n;
dfs(n, 1, 0);
if(f)
cout << "-1\n";
return 0;
}
提交结果
尾声
如果这篇博客对您(或您的团队)有帮助的话,就帮忙点个赞,加个关注!
最后,祝您(或您的团队)在 OI 的路上一路顺风!!!
再见~
ヾ( ̄▽ ̄)Bye~Bye~