題目鏈接:http://poj.org/problem?id=1011
題目大意是有一些等長的木棍,現將他們剪成小木棍,剪短後的小木棍最多有六十四根。題目輸入數據給定剪短後的小木棍的長度,求將其恢復成原來的木棍的最短長度。
本題剪枝要求較高:
剪枝(1):由於小木棍是由原來木棍剪短後得到的,那麼原來木棍的長度最短的也大於等於最長的小木棍,但不會超過所有小木棍長度之和;
剪枝(2):注意到原木棍長度是等長的,則將所有小木的長度加起來後,若不能按當前要搜索的值平均分配,那麼剪掉。
qsort(a,n,sizeof(int),cmp); //將所有小木棍按從大到小的順序排序
for (i=a[0];i<=sum;i++) //我們只需枚舉 最長的小木棍長度 到 所有小木棍長度之和。
{
if (sum%i==0&&dfs(0,0,0,i)) //如果所有小木棍長度之和不能按當前要搜索的值平均分配,那麼剪掉不搜索。因爲原木棍長度等長。
{
break;
}
}
剪枝(3):由於所有棒子已降序排序,在DFS時,若某根棒子不合適,則跳過其後面所有與它等長的棒子;
剪枝(4):最重要的剪枝,開始沒加這個剪枝,我TL,加了這個剪枝後,POJ上16ms。對於每次重新構造原始木棍長度時,若從第一根木棍開始搜索,無法組合成功,則不需要繼續往後搜索,因爲當前木棍若不能和其他木棍組合成原始木棍則就需要被捨棄,但是組成所有的原始木棍不能捨棄任何一根小木棍!#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 70
#define INF 0xfffffff
int a[MAX],flag[MAX];
int n;
int cmp(const void *a,const void *b)
{
return *(int *)b-*(int *)a;
}
int dfs(int tot,int s,int sum,int max) //四個參數分別爲tot-已經使用的小木棍數,s-上次搜索到第幾根小木棍
//sum-當前已經組合的小木棍長度,max-原始木棍的長度。
{
if (tot==n) //這是閥值,即所有木棍被用完,搜索結束
{
return 1;
}
int i,sample=-1;;
for (i=s;i<n;i++)
{
if (flag[i]||sample==a[i]) //如果當前木棍被用過了,
//或者與當前木棍等長的小木棍在此次組合成原始木棍中不能用時,當前木棍也不用檢索,跳過。剪枝(3);
continue;
flag[i]=1;
if (flag[i]&&sum+a[i]<max)
{
if (dfs(tot+1,i,sum+a[i],max))
{
return 1;
}
else
{
sample=a[i]; //這根木棍在此次組合中不能使用,則記錄,下次遇到等長跳過
}
}
else if (sum+a[i]==max) // 若組合成一根原始木棍
{
if (dfs(tot+1,0,0,max)) //則從從第一根木棍開始搜索,並且將記錄的小木棍組合的長度置零重新開始組合另一根原始木棍。
return 1;
else
{
sample=a[i];
}
}
flag[i]=0;
if (sum==0) //剪枝(4)若上面的木棍組合成功便會return,否則代表無法組合。
break;
}
return 0;
}
int main()
{
while (scanf("%d",&n)!=EOF)
{
if (n==0)
break;
int i,j,sum=0,max=-INF;
for (i=0;i<n;i++)
{
scanf("%d",&a[i]);
flag[i]=0;
sum+=a[i];
}
qsort(a,n,sizeof(int),cmp);
for (i=a[0];i<=sum;i++)
{
if (sum%i==0&&dfs(0,0,0,i))
{
break;
}
}
printf("%d\n",i);
}
return 0;
}