Problem
Before
做完16*16的数独后感觉这些搜索题都是小清新。
但是小木棍这题还是烦了我特别久,因此来写篇题解吧,总结一下。
Sulotion
这题无非就是两个剪枝,优化搜索顺序 + 去掉等效状态
优化搜索顺序
- 1.很明显,从大到小排,这样未来可选择的空间更少,搜索树更小。
去掉等效状态
-
1.假设一条木棍是由 1,3,6 拼接成的,1,6,3 和 3,1,6 这些顺序就是等效状态。我们只要保证每根大木棍内部编号顺序递增,那么就可以剪掉这些等效状态。
-
2.假设用长度为 5 的木棍拼接失败,那么就要跳过所以长度为 5 的木棍。所以跳过长度相等的并且失败了的木棍。
-
3.如果第一个木棍拼接失败,那么可以剪掉这个分支。反证法:条件:第一个木棍拼接失败。假设这个这个分支可以成功,那么将第一根木棍所在的那条大木棍放在第一个木棍这里,就可以拼接成功,条件不成立,故原命题成立。如果最后一个木棍拼接失败,那么可以剪掉这个分支,因为最后一个木棍是恰好可以拼好这个大木棍的,它的位置必须在这里,如果不行的话就说明这里不能成功拼接。
Code
Talk is cheap.Show me the code.
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x=0,f=1; char ch=getchar();
while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
return x * f;
}
const int N = 107;
int n,sum,len,ans;
int sticks[N];
bool vis[N];
bool cmp(int x,int y) {
return x > y;
}
bool Dfs(int tot,int now,int start) { //保证每根木棍内元素递增
if(tot*len == sum) return true;
if(now == len) return Dfs(tot+1,0,0);
for(int i=start;i<=n;++i) {
if(vis[i]) continue;
if(now+sticks[i] <= len) {
vis[i] = 1;
if(Dfs(tot,now+sticks[i],i+1)) return true;
vis[i] = 0;
if(!now) return false; //这是第一个木棍,
if(now+sticks[i] == len) return false; //这是最后一根木棍
int j = i;
while(j<=n && sticks[j]==sticks[i]) ++j;
i = j - 1; //跳过相同木棍
}
}
return false;
}
void work() {
memset(vis, 0, sizeof(vis)); sum = 0; ans = -1;
for(int i=1;i<=n;++i) {
sticks[i] = read();
sum += sticks[i];
}
sort(sticks+1, sticks+1+n, cmp);
for(int i=1;i<=sum;++i) {
if(sum%i == 0) {
len = i;
if(Dfs(0,0,0)) {
ans = i; break;
}
}
}
printf("%d\n",ans);
}
int main()
{
while(true) {
n = read();
if(n == 0) break;
work();
}
return 0;
}
Summary
-
看到搜索题第一步想优化枚举顺序
-
第二步剪去等效状态