蓝桥杯 试题 算法训练 无聊的逗 C++ 详解

题目:

逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中。不过他想到了一个游戏来使他更无聊。他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。

输入格式:第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。

输出格式:一个数,最大的长度。

样例输入:4(回车) 

                  1(空格)2(空格) 3(空格)1(回车)

样例输出:3

数据规模和约定:n<=15


前言:

蓝桥杯 试题 算法训练 无聊的逗 C++ 详解 - 未完善__Lyz_的博客-CSDN博客

上面这篇文章是我对 蓝桥杯 算法训练 《无聊的逗》的解析,不过是有问题的,但碰巧又通过的官网的测试样例,所以没有及时发现。

后经过一些网友的提醒才发现 自己解法有漏洞,但是那段时间比较忙,只偶尔回复一下网友,没有及时修改文章,之后给忘了。。。

(2022.03.25)今天上课时突然想起,在纸上推导一番后,目前可以解决所有情况。特地再写一篇文章,说明一下。

至于之前存在漏洞的文章,最不济也讲了一下DFS思想,我就先不删了,附上新文章的链接,之后再说。


开始:(读者应 对题目已有一定的了解)

对于本题,我们要做的就是将 一堆木棍 分为 相同的两堆。重点在于如何平均分?

根据上篇文章讲过的DFS思想,可以做到枚举所有情况。但还存在一个问题:无论如何分都无法均分为两堆。

例如:(1 1 2 3),然后我想的是:去除奇数最短的木棍,使木棍总和为偶数,"即可均分"。

但:(1 2 3 4 7)的时候就得去除最小的奇偶木棍1 2 了。

还有:(3 4 5 7)直接去除中间大小的5

似乎没有规律。。。 

这时,我们回顾上篇文章的DFS操作,对于某条木棍,除了选/不选,应该还有"放弃"的选项。

只不过之前文章的想法是直接在DFS外就想着把木棍们处理为 可均分的 大堆。

现在是 加多一个选择在DFS内。

还是举例,对于(1 1 2 3)。我们按照 选/不选 两种选择来操作的话。将

蓝桥杯 试题 算法训练 无聊的逗 C++ 详解_第1张图片

结果将是:0。即:无论如何都无法均分为两堆。但如果我们改进一下,加一种选择:"放弃"。情况又会不一样。

蓝桥杯 试题 算法训练 无聊的逗 C++ 详解_第2张图片

 当然了,没有举例完,剩下的结果显而易见,我就不一一举例了,主要是展示不同分支的情况。

 这时,得到的结果:3,也就是正确结果。

蓝桥杯 试题 算法训练 无聊的逗 C++ 详解_第3张图片

 如上图,顺着黄色箭头返回,发现其都曾有"放弃"的选择,即抛弃一个1,以达到可平均分为两堆的情况。


继续:

还有例子:3 4 5 7

哪怕 要去除的木棍5 不前不后,改进后的DFS仍可以枚举出其 被放弃 的可能。

就不一一图示了(我也想偷偷懒),主要是同学们自己动手写写更能深刻理解过程。


总结: 

综上所述,通过给DFS多加一种选择(双分支变三分支),则可以对所有木棍进行组合。(无需再做其他操作:去除木棍啥的,当然普通的优化是可以的,例如:最长的等于总和一半……)

DFS实现:参数(数组,数组长度,数组元素下标,左堆,右堆)

左堆:初始为所有木棍的总和

右堆:初始为0

通过dfs,左堆不断减少,右堆不断增加

当左堆等于右堆时,结束,比较/更新结果

当左堆小于右堆,剪枝。(之后dfs无意义)


附上代码:(更加简洁)

#include
using namespace std;
#include

//记录结果(不断刷新)
int MaxSum = 0;

//   深搜,	 数组,  长度,  下标, (总)左堆,      右堆
void dfs(int ii[], int n, int x, int left, int right = 0)
{
	//当满足 两堆相等 且 结果可更新
	if (left == right && MaxSum < left)
	{
		MaxSum = left;
		return;
	}
	//dfs剪枝(左堆小于右堆) 或 下标越界
	if (left < right || x >= n)
	{
		return;
	}
	//正常dfs
	else
	{
		dfs(ii, n, x + 1, left, right);//不选
		dfs(ii, n, x + 1, left - ii[x], right + ii[x]);//选
		dfs(ii, n, x + 1, left - ii[x], right);//放弃
		return;
	}
}

int main()
{
	int add = 0;
	int ii[15] = { 0 };

	int n; cin >> n;

	//录入数据 和 计算总和
	for (int i = 0; i < n; i++)
	{
		cin >> ii[i];
		add += ii[i];
	}

	//排序:升序
	sort(ii, ii + n, greater());

	//dfs
	dfs(ii, n, 0, add);

	//输出结果
	cout << MaxSum;

	return 0;
}

结束:

希望可以启发到各位 读者/同学 。

暂时就这样吧。


PS:本文配合之前的文章一起"食用"更佳。

蓝桥杯 试题 算法训练 无聊的逗 C++ 详解 - 未完善__Lyz_的博客-CSDN博客

你可能感兴趣的:(蓝桥杯,试题,算法训练,蓝桥杯,算法,c++)