Sticks
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 7223 Accepted Submission(s): 2115
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
6 5
深搜加剪枝
1. 首先要明白, Sum一定要能被 L 整除。
2. L 一定 大于等于 题目给出的最长的木棍的长度 Max。
由上述两点,我们想到,可以从 Max 开始递增地枚举 L,
直到成功地拼出 Sum/L 支长度为 L 的木棍。
搜索种的剪枝技巧:
3. 将输入的输入从大到小排序,这么做是因为一支长度为 K
的完整木棍,总比几支短的小木棍拼成的要好。
形象一些:如果我要拼 2 支长为8的木棍,第一支木棍我拼成 5 + 3
然后拼第二支木棍但是失败了,而我手中还有长为 2 和 1
的木棍,我可以用 5 + 2 + 1 拼好第一支,再尝试拼第二支,
这样做没意义,注定要失败的。我们应该留下 2+1 因为 2+1 比 3 更灵活。
4. 相同长度的木棍不要搜索多次, 比如:
我手中有一些木棍, 其中有 2 根长为 4 的木棍, 当前搜索
状态是 5+4+.... (即表示长度为 5,4,2 的三支拼在一起,
...表示深层的即将搜索的部分), 进行深搜后不成功,故
没必要用另一个 4 在进行 5+4+...
5. 将开始搜索一支长为 L 的木棍时,我们总是以当前最长的未
被使用的 木棍开始,如果搜索不成功,那么以比它短的开始
那么也一定不能取得全局的成功。因为每一支题目给出的木棍
都要被用到。如果,有
4
5 4 4 3 2
想拼成长为 6 的木棍,那么从 5 开始, 但是显然没有能与 5
一起拼成 6 的,那么我就没必要去尝试从 4 开始的,因为
最终 5 一定会被遗弃。在拼第 2 3 ... 支木棍时,一样。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int m;
int a[100];
bool use[100];
bool dfs(int nowlen,int nowxiang,int per,int tatalllen)
{
if(tatalllen==0) return 1;
int i;
for(i=nowxiang;i<m;i++)
{
if(use[i]) continue;
if(nowlen+a[i]>per) continue;
else if (nowlen+a[i]==per)
{
use[i]=1;
if(dfs(0,0,per,tatalllen-per))
return 1;
use[i]=0;
return 0; ////剪枝3
}
else if (nowlen+a[i]<per)
{
use[i]=1;
if(dfs(nowlen+a[i],nowxiang+1,per,tatalllen))
return 1;
use[i]=0;
if(nowlen==0) return 0; //剪枝5
while (a[i]==a[i+1]) i++; //剪枝4
}
}
return 0;
}
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int i;
while(scanf("%d",&m),m!=0)
{
int sum=0;
for(i=0;i<m;i++)
{
scanf("%d",&a[i]);
sum=sum+a[i];
}
sort(a,a+m,cmp);
//for(i=0;i<m;i++) printf("%d ",a[i]);
int start=a[0];
for(i=start;i<=sum;i++) //剪枝2
{
if(sum%i!=0) continue; //剪枝1
memset(use,0,sizeof(use));
if(dfs(0,0,i,sum))
{
printf("%d\n",i);
break;
}
}
}
return 0;
}