Sticks (经典搜索加剪枝)

Sticks

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other)

Total Submission(s) : 30   Accepted Submission(s) : 7

Font: Times New Roman | Verdana | Georgia

Font Size: ← →

Problem Description

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.

Input

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.

Output

The output file contains the smallest possible length of original sticks, one per line.

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

 

这道题问的是给你n个棍子,然后下面输入n个数,让你求他们最小组成棍子的长度是多少

是经典的搜索+剪枝;

首先我需要把棍子的总长求出来然后有六步剪枝:

1.从大到小排序,因为最小的棍长一定是大于等于最大的单位棍子长度。

2.如果我当前模除长度不为0那么这个一定也组不成棍长。

3.如果我找到了最小长度,那么就不找了结束就行

4.如果我vis= 0说明我没匹配到那么也不用找了,在拼凑一个新的长木棒时,如果所取的第一个短木棒就失败,那么意味着凭余下的短木棒不可能拼凑出长木棒。因为在取第一个短木棒时,该棒可以和当前可用的所有其他短木棒组合,如果这样都失败,意味根本不可能成功。直接返回上一层。

5.防止重复的查找

6.既然我已经找到了一组,因为有for我还要执行多余步骤所以我截止就行

 

# if 01
# include <iostream>
# include <cstring>
# include <algorithm>
# include<functional>

using namespace std;

const int maxn = 70;

bool book[maxn];
int m[maxn], n;
bool flag;

void dfs(int z, int len, int c, int vis, int f)//z是当前合成棍子的个数,len是目标棍子的长度,c是统计当前合成了几个棍子, vis是当前的长度, f是当前的下标
{
    if(z == c)
    {
        flag = true;
        return ;
    }
    for(int i = f; i < n; i++)
    {
        if(!book[i])
        {
            if(vis + m[i] == len)
            {
                book[i] = true;
                dfs(z, len, c + 1, 0, 0);
                if(flag)//第三步剪枝如果我找到了那么我就不找了,也就不用回溯了
                {
                    return ;
                }
                book[i] = false;
                return ;//第六步它在循环里面所以直接return防止费时间
            }
            else if(vis + m[i] < len)
            {
                book[i] = true;
                dfs(z, len, c, vis + m[i], i + 1);

                if(flag)//第三步剪枝如果我找到了那么我就不找了,也就不用回溯了
                {
                    return ;
                }
                book[i] = false;
                if(vis == 0) //第四步剪枝如果我这根棍子还是0说明找不到匹配的
                {
                    return ;
                }
                while(m[i] == m[i + 1])//第五步去重防止重复计算;
                {
                    i++;
                }
            }

        }
    }
}

int main(int argc, char *argv[])
{
    while(cin >> n, n)
    {
        int sum = 0;
        for(int i = 0; i < n; i++)
        {
            cin >> m[i];
            sum += m[i];
        }
        flag =  false;
        sort(m, m + n, greater<int>());//第一步剪枝,从大到小排序,最小的棍子也大于等于最大的单位长度
        for(int i = m[0]; i <= sum; i++)
        {
            if(!(sum % i))//第二步剪枝如果我能整除开说明可以结合整棍子
            {
                 memset(book, false, sizeof(book));
                 dfs(sum / i, i, 0,0,0);//
                 if(flag)
                 {
                     cout << i << endl;
                     break;
                 }
            }
        }

    }
    return 0;
}
# endif

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值