NC50243 小木棍

题目链接

题目

题目描述

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

输入描述

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

输出描述

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

示例1

输入

9
5 2 1 5 2 1 5 2 1

输出

6

备注

\(1 \leq N \leq 60\)

题解

知识点:DFS。

首先要枚举一个原长,再去dfs可行性,但这题关键在于剪枝。

优化:

  1. 原长要整除长度和
  2. 木棍从大到小排序,方便快速剪枝
  3. 访问过的木棍不再访问
  4. 拿取后长度超额的木棍不搜索
  5. 每次拼凑一根原棍从上一次访问的木棍之后开始,因为之前的都已经不可行了
  6. 一次失败后,后面全部相同长度的不要搜索
  7. 第一根或者最后一根失败代表上一根也失败直接跳出循环

以上优化缺一不可。

时间复杂度 \(O(?)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

int n, a[67];
int len;
bool vis[67];

bool dfs(int step = 0, int sum = 0, int pos = 0) {
    if (sum == len) {
        if (step == n) return true;
        else sum = 0, pos = 0;
    }
    for (int i = pos;i < n;i++) {
        if (vis[i] || sum + a[i] > len || i > 0 && a[i - 1] == a[i] && !vis[i - 1]) continue;
        vis[i] = 1;
        if (dfs(step + 1, sum + a[i], i + 1)) return true;
        vis[i] = 0;
        if (!sum || sum + a[i] == len) break;
    }
    return false;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    int sum = 0;
    for (int i = 0;i < n;i++) cin >> a[i], sum += a[i];
    sort(a, a + n, [&](int a, int b) {return a > b;});
    for (len = a[0];len <= sum;len++) {
        if (sum % len) continue;
        if (dfs()) {
            cout << len << '\n';
            break;
        }
    }
    return 0;
}
posted @ 2022-07-15 22:33  空白菌  阅读(40)  评论(0)    收藏  举报