其他分享
首页 > 其他分享> > 小木棍(dfs + 剪枝)

小木棍(dfs + 剪枝)

作者:互联网

原题链接
https://ac.nowcoder.com/acm/problem/50243

思路
这个题一开始考虑的是二分然后验证答案是否可行,但是后来发现要求的是最大长度的最小值,而且数据范围只有3000,所以可以从小到大枚举长度,第一次找到的就是最小的合法数据。首先来看以下性质:
1.枚举出来的最小长度一定是总长度的因子,不然除不尽。
2.如果拿一个长度为k的小木棒过来拼发现拼不上,那么和这个小木棒长度相同的都跳过。
3.在组装一根木棍的时候,如果发现第一根小木棒拿过来不能使得他组装完成,可以直接返回false,因为这一根小木棒在之后的拼接中无论如何都会放进来,而放在首位这样一个限制条件最宽的位置都不行,肯定会使得接下来的摆放出现问题。
4.在组装一根木棍的时候,如果发现最后一根小木棒拿过来不能使得他组装完成,可以直接返回false,因为如果选择几段更小的木棒来替换掉当前这段,后面有的可能就凑不出来了(这几段更小的可以分开放,满足更多的需要)。
5.在摆放同一根木棍时,记录当前使用到哪一根小木棒了,下一次就从这里开始扫因为不可能用到比前面还长的木棍。(不写这个剪枝会有一组数据过不了)

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100;

int a[N];
int n;
bool st[N];

bool cmp(int a, int b)
{
    return a > b;
}

// 当前还剩多少木棍可以用,枚举的长度,当前这根还剩多少没拼,当前扫到哪个位置了
bool dfs(int num, int len, int rest, int now)
{
    if (num == 0 && rest == 0) return true;  // 都用完了并且当前这根也拼好了
    if (rest == 0) rest = len, now = 1; // 当前这根拼好了,要到下一根
    
    for (int i = now; i < n; i ++ )
    {
        if (st[i]) continue;
        if (a[i] > rest) continue;
        st[i] = true;
        if (dfs(num - 1, len, rest - a[i], i)) return true;
        // 不能拼上
        st[i] = false;
        if (rest == len || rest == a[i])  // 这根是头或尾
            break;
        while (a[i + 1] == a[i]) i ++ ;
    }
    
    return false;
}

int main()
{
    cin >> n;
    int sum = 0;
    for (int i = 0; i < n; i ++ )
    {
        cin >> a[i];
        sum += a[i];
    }
    
    sort(a, a + n, cmp);
    
    for (int i = 1; i <= 3000; i ++ )
    {
        if (sum % i) continue;
        else
        {
            if (dfs(n, i, i, 0))
            {
                cout << i << endl;
                break;
            }
        }
    }
    
    return 0;
}






标签:剪枝,return,int,rest,len,木棒,dfs,木棍
来源: https://www.cnblogs.com/lautoh/p/14727669.html