poj 1011 Sticks(dfs+剪枝)
总时间限制: 1000ms 内存限制: 65536kB
描述
George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
输入
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
输出
The output should contains the smallest possible length of original sticks, one per line.
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
6
5
来源
Central Europe 1995
题意十分清晰,容易想到dfs,接下来无非如何剪枝的问题。
首先,剪枝思想不是很明晰显然,我是看了教学ppt才想到的,但是希望这回做到了这一次,以后能绷紧这根弦,遇到类似搜索马上想到这个剪枝。
注意到搜索重复情况十分多,不妨假设我们得到了一组解,那么我们这组解会被搜索到多次,以5 3 2 1 1 1这组数据为例,一个解为2+1,2+1,3,那么这个3可以第一次被搜到,也可以第二次第三次,而每一个2+1中2,1都不确定是哪一个2,1,而且也不确定是2+1还是1+2。那么搜索重复情况十分多了,我们从这一点入手剪枝。
剪枝1:
通过规定搜索合理顺序即可避免重复搜索,我们规定解必须由3,2+1这种情况搜出来(注意到最后一个2+1不必搜索了)。那么,也就是说优先安排大的小木棒在一根木棒的前面被搜到,这个很好理解。并且,前面的木棒的第一个小木棒一定比后面的第一个小木棒等长或长,因为可以规定这一种搜索顺序。也就是说先排序,再从前往后安排即可。
剪枝2:
避免等长小木棒的重复,比如某一个位置枚举过2,这个位置就不再枚举其它长为2的其它小木棒了。
教学ppt上讲述了4个剪枝,也许比我的剪枝更细腻,但是我没有仔细阅读教学ppt,我大致领会了它的精神,就是通过单调性规定搜索顺序剪枝。事实证明在这一思想指导下,即使剪枝不是很细致也可以0msAC。
Accepted 256kB 0ms 1573 B G++
#define MAX_N 64
#define TEST
#undef TEST
#include<stdio.h>
#include<stdlib.h>
int n,sum_len,len[MAX_N+1],every_len,num;
bool used[MAX_N+1];
bool legal;
int compare(const void* e1,const void* e2)
{
return *(int *)e2-*(int *)e1;
}
void check(int now,int now_len,int now_code)
{
if (legal)
return;
#ifdef TEST
if (every_len==3)
printf("*%d %d %d\n",now,now_len,now_code);
#endif
if (now_len==every_len)
{
check(now+1,0,0);
return;
}
if (now==num)
{
legal=true;
return;
}
if (now_len==0)
{
for (int i=1;i<=n;i++)
if (!used[i])
{
used[i]=true;
check(now,len[i],i+1);
used[i]=false;
return;
}
}
int j=0;
for (int i=now_code;i<=n;i++)
{
if (now_len+len[i]<=every_len && !used[i] &&
(len[i]!=len[j]))
{
j=i;
used[i]=true;
#ifdef TEST
if (every_len==3)
printf("**%d %d %d i=%d\n",now,now_len,now_code,i);
#endif
check(now,now_len+len[i],i+1);
used[i]=false;
}
}
return;
}
int main()
{
#ifdef TEST
freopen("input.txt","r",stdin);
#endif
while (scanf("%d",&n)&&n)
{
for (int i=1;i<=n;i++)
scanf("%d",&len[i]);
qsort(len+1,n,sizeof(int),compare);
#ifdef TEST
for (int i=1;i<=n;i++)
printf("%d ",len[i]);
printf("\n");
#endif
sum_len=0;
for (int i=1;i<=n;i++)
sum_len+=len[i];
for (int i=1;i<=n;i++)
used[i]=false;
for (every_len=len[1];every_len<=sum_len;every_len++)
if (sum_len%every_len==0)
{
legal=false;
num=sum_len/every_len;
check(1,0,1);
if (legal)
break;
}
printf("%d\n",every_len);
}
return 0;
}