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

Sticks

https://ac.nowcoder.com/acm/contest/1015/A

题目描述
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;
}
全部评论

相关推荐

求面试求offer啊啊啊啊:这个在牛客不是老熟人了吗
点赞 评论 收藏
分享
头像
05-16 12:47
已编辑
中国地质大学(武汉) Java
你出生在农村,与其它农村小孩子无异小学时你对成绩没有概念,只感觉上课不听课也是无聊,只知道不写完作业会被老师罚站一到考试,自己成绩总是名列靠前,即使偶尔落后,你也从不在意中学时你觉得课本的东西很简单,随便学学就会了,并没有大量刷题你总是想不通,那些所谓的数学物理中难题,明明是在送分,为什么你的同学总是想不出解题方法高中时这三年你过的不容易,晚睡早起,给了自己很多压力.但是你也发现自己是有些小聪明的,你感觉班里有些同学很刻苦,但成绩比你差远了。那些数学题和物理题的陷阱,同学一遍遍踩坑,但是你总能发现并避开它们.“为了父母的期盼,为了恩师的厚望,为了天赐的智慧,为了青春的理想......”“天行健...
创作助手_刘北:其实,这种已经是神童级别的了,不费吹灰之力就能拿到自己想要的东西,就像机器按照程序走了一遍,就像我小时候看爱情公寓,觉得他们都很惨,几个人只能挤在一个房间里合租,但是好在他们有一群非常好的朋友,随着时间的推移,慢慢长大了,在看爱情公寓的时候,觉得他们都很厉害,博士、留学生、***、电台公子,数学天才,任何一个都是我可望而不可即的,更别说可以在异地认识一群更好的朋友了,所以呢,人还是要自给自足,满足当下,不要攀比,意气风发的且有理想的18岁少年永远都存在,只不过随着时间的推移他被你包裹在了洋葱的最深处。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务