[题解] 洛谷P1120 小木棍 [数据加强版] 解题报告

题面链接:https://www.luogu.com.cn/problem/P1120

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

共二行。

第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65N≤65

(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)

第二行为N个用空个隔开的正整数,表示N根小木棍的长度。

输出格式
一个数,表示要求的原始木棍的最小可能长度

输入输出

输入

9
5 2 1 5 2 1 5 2 1

输出

6

基本思路

因为这些小木棍的原始长度都是相同的,所以我们可以枚举小木棍的原始长度cur,然后再用所有的木棍来拼凑出整数个长cur的木棍

如果能拼出,则说明这个cur就是一个解,否则更改cur的值,再次拼凑。

综上,解题的思路大纲基本有了,枚举cur循环就可以解决,确定cur是否为一个解的过程我们尝试用 dfs 来解决

(题外话:dfs 一定要深入理解,要清楚为什么用dfs,dfs本质是递归,递归的本质就是回溯。我们如果递归进入了n条岔路口后发现当前这条路不通,我们可以回到上一个岔路口重新选择另外一条道路,如果不使用递归,那么就 game over 了,你需要回到起点重新走一遍,然后在选择另一条岔路口。 对的,就是这样,你懂我意思。我们接着往下看)

基本实现流程

一、主函数内

  1、将木棍读入后从大到小排序存到数组q中。注意:读入时只读入长度小于等于50的木棍并cnt计数

  2、由题面可知原始的木棍一定被砍了大于1刀,所以原始最短的木棍长度一定比被砍后的木棍长度大,所以取被砍后的木棍中的最大值 + 1为假设的最短长度cur,然后慢慢累加1,逐一判断

  3、假设了最短的长度cur后,就利用dfs来判断这个长度是否能拼凑出来,以下是dfs的流程

二、dfs函数内

  1、先确定dfs所需要使用的参数,(int 当前长度, int 最终长度, int 总长度, int 利用完的木棍的数量, int 总木棍数)

  其中总长度sum在读入的时候可以存为全局变量,总木棍数cnt也在读入时有了,所以最终的dfs参数为:
  dfs ( int 当前长度, int 最终长度, int 利用完的木棍的数量 );
  bool dfs ( int cur, int fin, int remain );

  2、函数内的思路为:
  用bool数组st记录木棍是否被使用过

  每次“逐渐逼近”时都将remain加一

  基准情形的设置也可看下述序号(2)(3):

  (1)、从大到小拼凑木棍(拼凑后st设置为true)

  (2)、如果拼凑后木棍长度等于给定长度则再次判断是否已利用完所有木棍,已用完返回true否则返回说明已经凑齐了一根木棍,继续去凑下一根,将cur清零,继续dfs

  (3)、如果小于给定长度则无需理会

  (4)、如果大于给定长度则返回false

好了,按以上的思路打完代码,超时妥妥的,这是框架,剩下的就是疯狂剪枝/魔鬼笑

以上的代码如下:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100;

// cnt 为输入的木棍总数,sum 为所有木棍总长度
int n, cnt = 0, sum = 0;	
int q[N];
bool st[N];

// cur 为当前长度,fin 为最终要拼凑好的单根长度,remain 为已消耗的木棍数量
bool dfs ( int cur, int fin, int remain )
{
//	此基准情形的逻辑请参考实现流程
	if ( cur == fin )
	{
		if ( remain == cnt ) return true;
		else {
			return dfs ( 0, fin, remain );
		}
	} else if ( cur > fin ) {
		return false;
	}

	for ( int i = 0; i < cnt; i++ )
	{
		if ( !st[i] )
		{
			st[i] = true;
			if ( dfs ( cur + q[i], fin, remain + 1 ) ) return true;
			st[i] = false;
		}
	}
	
	return false;
}

int main ( void )
{

	cin >> n;

	for ( int i = 0; i < n; i++ )
	{
		int x;
		cin >> x;

		if ( x <= 50 ) q[cnt++] = x, sum += x;
	}

	sort ( q, q + cnt, greater<int>() );

	for ( int i = q[0] + 1; i <= sum; i++ )
	{
		if ( sum % i == 0 )
		{
			if ( dfs ( 0, i, 0 ) )
			{
				cout << i << endl;
				break;
			}
		}
	}

	return 0;
}

接下来进入优化环节

作者:Jude_Zhang
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用BY-NC-SA 许可协议。转载请注明出处!
支持博主:如果您觉得文章对您有帮助,可以点击文章下方赞一下。您的鼓励是博主的最大动力!
原文地址:https://www.cnblogs.com/judezhang/p/14558543.html