少林神棍
DFS 一个非常经典的题目。北大POJ训练题
有着非常神奇的剪枝.
题目意思:给n个小木棒 要求把N个小木棒拼凑成长度 一样的木棍。
利用DFS 进行每个长度的枚举。
剪枝三:拆解拼凑第i个木棍的第一个木棒这是没有必要的因为拆掉第一个木棒说明当前剩余的木棒已经使得第i个木棍已经是拼凑不成枚举的长度
剪枝四:拆解第i个木棍的最后一个木棒k用若干个更小的木棒来替换。
这样是不合理的!
假设当前枚举的长度是成立的结果,那么k木棒一定会出现在后面拼凑木棍之中。那么用k木棒去替换i木棍最后一个位置也是成立的,说明这个替换策略是正确!
剪枝五 把每一个木棍按从大到小去拼,这样可以减少重复的计算。
#define maxn 10000
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <climits>
#include <vector>
#include <cstring>
#include <fstream>
#include<functional>
using namespace std;
vector<int> tick;
bool vis[64];//i木棒是否被使用
int n,T,St;
bool dfs(int m, int L)
{
if (m == 0 && L == 0)
{
return true;
}
if (L == 0)
{
L = T;
}
int SN = 0;
if (L != T)
{
SN = St + 1;//剪枝5
}
for (int i = SN; i < n; i++)
{
if (!vis[i]&&tick[i]<=L)
{
if (i > 0)
{
if (vis[i - 1] == false && tick[i - 1] == tick[i])
{
continue; //剪枝 2 如果在拼凑x个木棍的时候用了第i木棒并且拼凑失败了没有必要再枚举第i个木棒
}
}
vis[i] = true;//i木棒用过了
St = i;//记录当前枚举到了i个木棍的位置
if (dfs(m - 1, L - tick[i]))
{
return true;
}
else
{
vis[i] = false;
if (L==tick[i]||L == T) //剪枝3,4 如果L拆了第一个木棒和最后一个木棍的话那么搜索变的没有必要性
{
return false;
}
}
}
}
return false;
}
int main()
{
while (~scanf("%d",&n)&&n)
{
int sum = 0;
tick.clear();
for (int i = 0; i < n; i++)
{
int s;
scanf("%d", &s);
tick.push_back(s);
sum += s;
}
sort(tick.begin(), tick.end(),greater<int>());//木棍从大到小排序
int i;
for (i = tick[0]; i <=sum/2 ; i++)
{
if (sum%i)//如果枚举的长度不足以被sum整除那是不可能拼成长度一样的木棍
{
continue;
}
memset(vis, false, sizeof(vis));
T = i;
if (dfs(n, i))
{
printf("%d\n", i);
break;
}
}
if (i > sum / 2)
{
printf("%d\n", sum);
}
}
return 0;
}