这道题与poj2362类似,对剪枝的要求更多了。题目大意为原本有许多等长的棍子,被切碎了,现在要求出原来棍子最小的可能长度。
依旧是将棍子按照从大到小排序,减少递归次数。
因为所有棍子都要用到,所以原来棍子长度一定>=最长的一根棍子。求出棍子总长度sum,然后从最长的一根棍子长度开始,向上枚举,当sum/%i==0 时 ,说明原来棍子有可能是这么长,再进行DFS 从而判断。
以下为代码:
<pre name="code" class="cpp">#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int a[80],b[80],n,flag;
bool cmp(int x,int y)
{
return x>y;
}
void dfs(int len,int i,int step,int num,int k) //len为当前长度,i为目标长度,step当前边数,num目标边数,k记录下一根棍子搜索位置
{
if(step==num){ //递归边界
flag=1;
return ;
}
int ret=-1;
for(int j=k;j<n;j++){
if(flag)return ;
if(b[j] || a[j]==ret) continue; //剪枝:ret记录上一根不合要求的棍子
b[j]=1; //标记
if(len+a[j]<i) dfs(len+a[j],i,step,num,j),ret=a[j];
if(len+a[j]==i) dfs(0,i,step+1,num,0),ret=a[j];
b[j]=0; //还原
if(len==0) return ; //重要剪枝:若凑大棍子的第一根小棍子不合要求,则不要继续往后搜索,直接返回
}
}
int main()
{
// freopen("in.txt","r",stdin);
while(1){
scanf("%d",&n);
if(!n) break;
memset(a,0,sizeof(a));
int sum=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
int num;
sort(a,a+n,cmp);
for(int i=a[0];i<=sum;i++){ //寻找最小的可能的原长
if(sum%i==0){
flag=0;
num=sum/i;
memset(b,0,sizeof(b));
dfs(0,i,0,num,0);
if(flag)break;
}
}
printf("%d\n",sum/num);
}
return 0;
}