CSP-J系列【2024】P11229 [CSP-J 2024] 小木棍题解

题目描述

小 S 喜欢收集小木棍。在收集了 n 根长度相等的小木棍之后,他闲来无事,便用它们拼起了数字。用小木棍拼每种数字的方法如下图所示。

现在小 S 希望拼出一个整数,满足如下条件:

  • 拼出这个数恰好使用 n 根小木棍;
  • 拼出的数没有前导 0;
  • 在满足以上两个条件的前提下,这个数尽可能小。

小 S 想知道这个数是多少,可 n 很大,把木棍整理清楚就把小 S 折腾坏了,所以你需要帮他解决这个问题。如果不存在正整数满足以上条件,你需要输出 −1 进行报告。

输入格式

本题有多组测试数据。

输入的第一行包含一个正整数 T,表示数据组数。

接下来包含 T 组数据,每组数据的格式如下:

一行包含一个整数 n,表示木棍数。

输出格式

对于每组数据:输出一行,如果存在满足题意的正整数,输出这个数;否则输出 −1。

输入输出样例

输入 #1复制

5
1
2
3
6
18

输出 #1复制

-1
1
7
6
208

说明/提示

【样例 1 解释】

  • 对于第一组测试数据,不存在任何一个正整数可以使用恰好一根小木棍摆出,故输出 −1。
  • 对于第四组测试数据,注意 0 并不是一个满足要求的方案。摆出 9、41 以及 111 都恰好需要 6 根小木棍,但它们不是摆出的数最小的方案。
  • 对于第五组测试数据,摆出 208 需要 5+6+7=18 根小木棍。可以证明摆出任何一个小于 208 的正整数需要的小木棍数都不是 18。注意尽管拼出 006 也需要 18 根小木棍,但因为这个数有前导零,因此并不是一个满足要求的方案。

【数据范围】

对于所有测试数据,保证:1≤T≤50,1≤n≤105。

测试点编号n≤特殊性质
120
250
3103A
4,5105A
6103B
7,8105B
9103
10105

特殊性质 A:保证 n 是 7 的倍数且 n≥100。

特殊性质 B:保证存在整数 k 使得 n=7k+1,且 n≥100。

题目概述与分析

小木棍问题是2024年CSP-J竞赛中的一道经典题目,考察选手对搜索算法和剪枝优化的掌握。题目描述有n根长度不同的小木棍,需要将它们拼接成若干根长度相同的长木棍,求可能的最小原始长木棍长度。

这个问题可以建模为组合优化问题,需要综合考虑所有可能的组合方式。我们将介绍两种不同的解法:深度优先搜索(DFS)剪枝法和动态规划法。

解法一:DFS剪枝法

算法思路

  1. 使用深度优先搜索尝试所有可能的组合
  2. 通过多种剪枝策略优化搜索效率
  3. 从最大可能长度开始向下搜索
  4. 时间复杂度取决于剪枝效果,最坏情况下O(n!)
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

vector<int> sticks;
vector<bool> used;
int n, total_len, max_len;

bool dfs(int cnt, int cur_len, int start, int target) {
    if (cnt == total_len / target) return true;
    if (cur_len == target) return dfs(cnt + 1, 0, 0, target);
    
    for (int i = start; i < n; ++i) {
        if (!used[i] && cur_len + sticks[i] <= target) {
            if (i > 0 && sticks[i] == sticks[i-1] && !used[i-1]) continue;
            used[i] = true;
            if (dfs(cnt, cur_len + sticks[i], i + 1, target)) return true;
            used[i] = false;
            if (cur_len == 0) break;
        }
    }
    return false;
}

int main() {
    cin >> n;
    sticks.resize(n);
    used.resize(n, false);
    total_len = 0;
    max_len = 0;
    
    for (int i = 0; i < n; ++i) {
        cin >> sticks[i];
        total_len += sticks[i];
        max_len = max(max_len, sticks[i]);
    }
    
    sort(sticks.begin(), sticks.end(), greater<int>());
    
    for (int len = max_len; len <= total_len; ++len) {
        if (total_len % len != 0) continue;
        fill(used.begin(), used.end(), false);
        if (dfs(0, 0, 0, len)) {
            cout << len << endl;
            return 0;
        }
    }
    
    cout << total_len << endl;
    return 0;
}

该实现使用DFS加多种剪枝策略,包括排序优化、跳过重复值和提前终止等,能有效提高搜索效率。

解法二:动态规划法

算法思路

  1. 将问题转化为背包问题的变种
  2. 使用位运算状态压缩
  3. 记录可达的长度组合
  4. 时间复杂度O(n*sum),适合sum较小的情况
#include <iostream>
#include <vector>
#include <algorithm>
#include <bitset>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> sticks(n);
    int sum = 0;
    
    for (int i = 0; i < n; ++i) {
        cin >> sticks[i];
        sum += sticks[i];
    }
    
    sort(sticks.begin(), sticks.end());
    
    for (int len = sticks.back(); len <= sum; ++len) {
        if (sum % len != 0) continue;
        
        int k = sum / len;
        vector<bitset<100>> dp(k + 1);
        dp[0][0] = 1;
        
        for (int stick : sticks) {
            for (int i = k; i >= 0; --i) {
                for (int j = len; j >= 0; --j) {
                    if (dp[i][j]) {
                        if (j + stick <= len) {
                            dp[i][j + stick] = 1;
                        }
                        if (i + 1 <= k && stick <= len) {
                            dp[i + 1][stick] = 1;
                        }
                    }
                }
            }
        }
        
        if (dp[k][0]) {
            cout << len << endl;
            return 0;
        }
    }
    
    cout << sum << endl;
    return 0;
}

动态规划解法通过状态压缩记录可达的长度组合,适合小规模数据,但空间复杂度较高。

算法对比分析

方法时间复杂度空间复杂度适用场景
DFS剪枝法取决于剪枝效果O(n)数据规模中等,剪枝有效
动态规划法O(n*sum)O(k*len)sum值较小的情况

关键问题详解

剪枝策略优化

  1. 从大到小排序木棍,优先尝试长木棍
  2. 跳过相同长度的重复尝试
  3. 当前组合失败时及时回溯
  4. 限制搜索的起始位置避免重复

边界情况处理

  1. 所有木棍长度相同的情况
  2. 无法分割的情况
  3. 极端大规模数据测试
  4. 包含长度为1的木棍的情况

性能优化建议

  1. 预处理时计算所有可能的候选长度
  2. 使用更高效的数据结构存储状态
  3. 并行化处理可能的候选长度
  4. 提前终止不可能的组合

数学原理分析

  1. 组合数学中的分割问题
  2. 数论中的因数分解应用
  3. 搜索算法的剪枝理论
  4. 动态规划的最优子结构性质

实际应用价值

这类算法在以下领域有重要应用:

  1. 资源分配与调度
  2. 货物装载优化
  3. 时间表安排
  4. 工业生产中的材料切割

扩展思考

  1. 如果木棍有不同颜色限制如何处理?
  2. 如果允许部分不匹配的情况怎么解决?
  3. 如何扩展到三维空间的材料分割?
  4. 多目标优化情况下如何调整算法?

掌握这类组合优化问题的解法,不仅能提升竞赛能力,也对解决实际工程问题有很大帮助。建议先理解基础算法原理,再针对具体问题特点进行优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值