小木棍(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