题解 | #小木棍# DFS+经典的剪枝 1秒限时
小木棍
https://ac.nowcoder.com/acm/problem/50243
1
2013年就遇到过,待深入理解。 QQ张学锋?木棍和,pat停车场,;
这个题目估计某些人也想了很久,关键时间总是卡在一秒
内。
每一个剪枝都不能少。
1.1.1 反复理解4个剪枝
上述思想已经可以做出正确的答案了。但是不考虑剪枝的话这题会TLE。所以我们必须做出一些剪枝策略:
- 我们知道小棍是由
n
根大棍砍开得到的。由于这个n
是一个正整数,所以大棍的长度一定是总长度的因子。即枚举答案的时候,若当前枚举出的数值不能被总长度整除,说明这不是一个可能的答案。直接跳过,都不需要进dfs了。 - 若将当前正在尝试的这根小棍放置在大棍打头的位置发现不可行,说明当前假设出的大棍长度是不可行的。因为这根小棍总要被用上,而你放在第一个位置已经不可行了!(第一个位置的限制条件是最宽松的,因为此时剩余的长度最宽)放后面就更不可行了!此时直接结束dfs,不需要再试探下去了!
- 同理,若当前小棍放在大棍末尾的位置刚好能放下但后续的拼接无法恰好拼上,说明当前假设出来的大棍长度不可行(因为这根小棍总要被放下!!!)跳过这个
len
- 当前长度的小棍试探过后,与这一根相同长度的棍子就不用再试了
1.1.2 第一根或最后一根小棍不行 回退
2 code
#include<bits/stdc++.h>
using namespace std;
int n;
int v[65], a[65];
bool cmp(int b, int c){
return b > c;
}
bool dfs(int num, int rest, int len, int now){
//剩下的小棍子数量,当前大棍剩下的长度
//枚举的大棍长度,拼接当前大棍的第now根小棍子
if(0 == num && 0 == rest) //小棍子全部拼完且没有剩余长度
return true;
if(0 == rest){ //拼下一根大棍
rest = len;
now = 0;
}
for(int i = now; i < n; i++){
if(v[i]) continue; //已使用
if(a[i] > rest) continue;//这根小棍大于剩余长度,放不下,pass
v[i] = 1;
if(dfs(num - 1, rest - a[i], len, i))
return true;
v[i] = 0;//回溯
if(rest == len || rest == a[i])
break; //第一根或最后一根小棍不行,说明当前枚举的len行不通
while(a[i] == a[i + 1])
i++; //与当前长度一致的小棍就不需要再尝试了
}
return false;
}
int main(){
cin>>n;
int sum = 0, i;
for(i = 0; i < n; i++){
cin>>a[i];
sum += a[i];
}
sort(a, a + n, cmp); //从最长的小棍子开始拼接
for(i = a[0]; i <= sum; i++){//从最长的小棍长度开始枚举
if(sum % i != 0) continue;//假设的大棍不能被总长整除,不可行!
if(dfs(n, i, i, 0)) break;
}
cout<<i<<endl;
return 0;
}