洛谷 P7071 [CSP-J2020] 优秀的拆分(T1)

目录

题目传送门

算法解析

做法

递归参数

递归边界

递归

输出

题解

AC 代码

提交结果

尾声


题目传送门

[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~

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值