307 Sticks(*****)经典DFS+剪枝

/*
推荐题型:五星		经典DFS + 剪枝
题意:长度相同的木棒被分成许多段,求木棒原来状态的最短长度
此题重在剪枝,主要剪枝思路有:
使用数组a存储被分解成的各个部分的长度,木棒的每段被称之为s
①搜索范围从max到sum,且必须能被sum整除
②为避免重复,组合成木棒的s从大到小排序,每根木棒的第一个s,也要求从大到小排序
③在某层搜索中,如果a[i]没有使用,且a[i]==a[i+1],则a[i+1]也要舍弃
④搜索中,木棒的第一个s,a[i]为可满足的最大值,如果尝试失败,则直接退出,返回到上一层搜索
完全参考:心如止水C++博客
*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
 // #define LOCAL

#ifdef LOCAL
  #include<time.h>
#endif
int a[60];
bool used[60];
int aim,num;
int n;
int cmp(const void *a,const void *b)
{
	int *pa=(int *)a;
	int *pb=(int *)b;
	return (*pb)-(*pa);
}
bool dfs(int stick,int len,int pos)
{
	bool sign=(len==0?true:false);
	if(stick==num) return true;//这里实际只需要到num即可,因为aim可被sum整除,所以后面的不需要再计算
	for(int i=pos+1;i<n;i++)
	{
		if(used[i]) continue;
		if(len+a[i]==aim)
		{
			used[i]=true;
			if(dfs(stick+1,0,-1))
				return true;
			used[i]=false;
			return false;

		}
		else if(len+a[i]<aim)
		{
			used[i]=true;
			if(dfs(stick,len+a[i],i))
				return true;
			used[i]=false;
			if(sign) return false;
			while(i<n-1 && a[i]==a[i+1])i++;
		}
	}
	return false;
}
int main()
{
	//freopen("data.in","r",stdin);
	while(scanf("%d",&n) && n)
	{
		int max,sum;
		max=sum=0;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]>max)
				max=a[i];
			sum+=a[i];
		}
		qsort(a,n,sizeof(a[0]),cmp);
		for(aim=max;aim<=sum;aim++)
		{
			if(sum%aim==0)
			{
				num=sum/aim;
				memset(used,0,sizeof(used));
				if(dfs(1,0,-1))
				{
					printf("%d\n",aim);
					break;
				}
			}
		}
	}
	 #ifdef LOCAL
      printf("used time = %.3lf\n",(double)clock()/CLOCKS_PER_SEC);
    #endif
	return 0;
}

你可能感兴趣的:(307 Sticks(*****)经典DFS+剪枝)