小木棍


题目链接:https://ac.nowcoder.com/acm/problem/50243

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入描述

第一行为一个单独的整数N表示砍过以后的小木棍的总数。第二行为N个用空格隔开的正整数,表示N根小木棍的长度。 1≤N≤60

输出描述

输出仅一行,表示要求的原始木棍的最小可能长度。

示例

输入

9
5 2 1 5 2 1 5 2 1

输出

6

分析

本题采用深搜加剪枝的算法。
首先,N个小木棍的长度均为整数,因此原长度必为整数(若原长为小数,则进行分割必产生小数长度的木棍)。
题目要求找出可能的最小木棒长度,所以我们可以想到通过深搜一个数一个数的去验证,但显然单纯深搜会相当耗时,最多有
60个木棍,不优化的话每确定一个木棍都要遍历60次,总共确定60次,且仅仅完成一个长度的试探。
因此,我们就要通过一些优化技巧和剪枝来提高效率。

优化技巧:

  • 深搜时从长度大的木棍开始试探,可以减小搜索树的规模。
  • 深搜函数多设置一个参数。我们木棍长度由大到小的顺序来试探的,在一个尚未拼完整的木棒中,下一层要试探的木棍必然是比该层短的木棍(我们将存储木棍长度的数组排序),传入该层木棍的下标,下一层从该下标开始遍历,可以减少循环次数。

剪枝:

  • 所有木棍的长度均小于等于原长,所以我们试探的下界为所有木棍长度的最大值。
  • 求得木棍长度总和,对于一个可能的原长值,若总和是该值的整数倍,则有可能(前面已述原长必为整数,总和除以该值即为原长),否则直接跳过。
  • 某一层某木棍试探失败后,若该木棍试探位置位于木棒首部或尾部,则直接结束该层,回退到上一层换值试探。因为此时试探的情况为:已经完整的拼好了几个木棒,没有拼一半的木棒,继续在该层换小长度的木棍再试探没有意义(必失败),问题必然出自前面。

以上各方面实现到位,即可AC。

AC代码


#include <iostream>
#include <algorithm>
#include <cstring>
#include <functional>
using namespace std;
int N;
int a[60];      //存木棍长度
bool visited[60];
bool dfs(int num, int len, int res, int now) //剩余多少木棍 假设的木棍长度 
{                                            //当前正拼接木棍的长度 下一层递归的起始搜索序号
    if (res == 0 && num == 0)
        return true;
    if (res == 0)
    {
        res = len;
        now = 0;
    }
    for (int i = now; i < N; i++)
    {
        if (visited[i])
            continue;
        if (a[i] > res)
            continue;
        visited[i] = true;
        if (dfs(num - 1, len, res - a[i], i + 1))
            return true;

        visited[i] = false;
        if (a[i] == res || res == len)  //分别表示木棒尾端 和木棒头部拼接失败
            return false;
        while (a[i] == a[i + 1])        //越过长度相同的木棍
            i++;
    }
    return false;
}
int main()
{
    int sum = 0;
    cin >> N;
    for (int i = 0; i < N; i++)
    {
        cin >> a[i];
        sum += a[i];
    }
    sort(a, a + N, greater<int>());
    for (int i = a[0]; i <= sum; i++)
    {
        if (sum % i == 0 && dfs(N, i, i, 0))
        {
            cout << i << endl;
            break;
        }
    }
    return 0;
}

文章作者: Kong Aobo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kong Aobo !
  目录