题解|《算法竞赛进阶指南》 Sticks

本文介绍了一种通过已知切割后木棍长度集合,逆推原始木棍最小可能长度的方法。利用枚举与剪枝策略,优化算法效率,确保在多组数据下快速求解。

题目描述
George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
输入描述:
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
输出描述:
The output should contains the smallest possible length of original sticks, one per line.

思路
1.因为不知道原始木棒的长度len,但是知道每根小木棍的长度,小木棒最长的时候就是一根的时候也就是长度等于所有的小木棍的长度总和sum。所以,我们可以枚举长度len。这样子就把查询问题转化成了判断该长度是否可行的问题。
2.在长度确定的同时也就确定了小木棒的数量cnt=sum/len,那么这个就可以作为合法标志的判断条件:在所有的小木棍都用完的情况下拼成了cnt个长度相等的小木棒。
3.这个时候就要思考这道题要让计算机做什么?
1.枚举长度len;
2.用之前还没有使用过的小木棍拼凑小木棒;
3.判断该长度方案是否可行。
4.考虑上面的那些状态是需要在搜索中考虑到的?
每根小木棒的长度len; 已经拼成了多少根小木棒; 每一根小木棍的状态
因为上面的sum最大是64*50(这个数字还是蛮大的),那么最坏的打算是每一个长度都要考虑进去,每一个长度的每一种拼法也都要尝试一遍,这样的话最后得出答案的话就是会需要很长时间的,更何况给出的数据是多组数据,所以我们要考虑剪枝优化。

剪枝
1.搜索顺序的优化:我们可以按照小木棍的长度进行排序,从大到小,若填上最长的之后没有可以匹配的话,那么这个长度绝对是不合法的。(大块一定比小块需要搜索的次数少)
2.跳过相同木棒
3.如果是第一个木棒失败,则一定失败
4.如果是最后一个木棒失败,则一定失败

完整C++版AC代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 70;
int n;
int sum, length;
int stricks[N];
bool st[N];
//u为当前第几段了,cur记录当前段的长度,starct为上一次已经选过的值
bool dfs(int u, int cur, int start)
{
    if (u * length == sum) return true;
    if (cur == length)return dfs(u + 1, 0, 0);//如果发现这一段已经用完了,下一段接着来.

    for (int i = start; i < n; i++)
    {
        if (st[i]) continue;
        int l = stricks[i];
        if (cur + l <= length)
        {
            st[i] = true;
            if (dfs(u, cur + l, i + 1)) return true;
            st[i] = false;

            if (!cur) return false; // 剪枝3 如果是第一个木棒失败,则一定失败

            if (cur + l == length) return false;// 剪枝4 如果是最后一个木棒失败,则一定失败
             // 剪枝2 跳过相同木棒
            int j = i;
            while (j < n && stricks[j] == l) j++;
            i = j - 1;
        }
    }
    return false;
}

int main() {
    ios::sync_with_stdio;

    while (cin >> n, n) {

        sum = 0, length = 0;
        memset(st, false, sizeof st);

        for (int i = 0; i < n; i++) {
            cin >> stricks[i];
            if (stricks[i] > 50) continue;
            sum += stricks[i];
            length = max(length, stricks[i]);
        }

        sort(stricks, stricks + n); // 剪枝:优化搜索顺序
        reverse(stricks, stricks + n);

        for (int i = 0; i < n; i++)
            if (stricks[i] > 50)
                st[i] = true;

        while (true)
        {
            if (sum % length == 0 && dfs(0, 0, 0))
            {
                cout << length << endl;
                break;
            }
            length++;
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/Mashiro-zBlog/p/11439298.html

### 算法竞赛进阶指南题库资源 对于算法竞赛进阶指南的题库资源,可以通过以下方式获取或参考相关内容。以下是关于下载或在线查看的详细说明: #### 1. 在线阅读 部分网站提供了《算法竞赛进阶指南》的配套题解和练习内容,用户可以在线访问这些资源。例如: - **AcWing** 提供了算法竞赛进阶指南》相关的课程和题目解析[^1]。用户可以在其平台上找到对应的章节和习题,并通过视频讲解加深理解。 - **牛客竞赛** 平台也提供了一些类似的题库资源,尽管并非完全匹配该书的内容,但其题目分类和难度设置可以帮助用户进行系统化的练习[^1]。 #### 2. PDF 或文档下载 如果需要下载 PDF 或其他格式的题库文件,建议通过以下途径: - **官方渠道**:购买正版书籍时,通常会附带电子版资源链接或二维码,扫描后即可获得合法的 PDF 文件[^3]。 - **学术资源网站**:一些教育类网站可能提供公开的题库文件,但需要注意版权问题,确保下载来源合法。 - **社区分享**:如 GitHub 上可能存在由爱好者整理的题库资源,但需谨慎甄别内容的完整性和准确性。 #### 3. 示例代码 以下是一个基于《算法竞赛进阶指南》中分组背包问题的经典实现代码示例,可供参考: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; const int N = 305; vector<int> G[N]; int f[N][N], w[N]; void dfs(int a) { for (int i = 0; i < G[a].size(); i++) { int p = G[a][i]; dfs(p); for (int j = m - 1; j > 0; j--) { for (int k = 1; k <= j; k++) { f[a][j] = max(f[a][j], f[a][j - k] + f[p][k]); } } } for (int i = m; i > 0; i--) { f[a][i] = f[a][i - 1] + w[a]; } } int main() { int n, m; cin >> n >> m; for (int i = 1; i <= n; i++) { int a; cin >> a >> w[i]; G[a].push_back(i); } m++; dfs(0); cout << f[0][m] << endl; return 0; } ``` #### 注意事项 在寻找题库资源时,务必注意以下几点: - 避免从非法渠道下载资源,以免侵犯版权。 - 确保下载的文件安全可靠,避免潜在的病毒或恶意软件风险。 - 如果无法找到完整的题库文件,可以通过上述在线平台补充学习内容[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值