题解|《算法竞赛进阶指南》 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;
}

猜你喜欢

转载自www.cnblogs.com/Mashiro-zBlog/p/11439298.html