Description
Input
Output
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 5
重要减枝
越长的木棍对后面木棍的约束力越大,因此要把小木棍排序,按木棍长度从大到小搜索,这样就能在尽可能靠近根的地方剪枝。(剪枝一)
如果当前木棍能恰好填满一根原始木棍,但因剩余的木棍无法组合出合法解而返回,那么让我们考虑接下来的两种策略,一是用更长的木棍来代替当前木棍,显然这样总长度会超过原始木棍的长度,违法。二是用更短的木棍组合来代替这根木棍,他们的总长恰好是当前木棍的长度,但是由于这些替代木棍在后面的搜索中无法得到合法解,当前木棍也不可能替代这些木棍组合出合法解。因为当前木棍的做的事这些替代木棍也能做到。所以,当出现加上某根木棍恰好能填满一根原始木棍,但由在后面的搜索中失败了,就不必考虑其他木棍了,直接退出当前的枚举。(剪枝二)
显然最后一根木棍是不必搜索的,因为原始木棍长度是总木棍长度的约数。(算不上剪枝)
考虑每根原始木棍的第一根木棍,如果当前枚举的木棍长度无法得出合法解,就不必考虑下一根木棍了,当前木棍一定是作为某根原始木棍的第一根木棍的,现在不行,以后也不可能得出合法解。也就是说每根原始木棍的第一根小木棍一定要成功,否则就返回。(剪枝四)
剩下一个通用的剪枝就是跳过重复长度的木棍,当前木棍跟它后面木棍的无法得出合法解,后面跟它一样长度的木棍也不可能得到合法解,因为后面相同长度木棍能做到的,前面这根木棍也能做到。(剪枝五)
不明白自己感觉各种减枝都用上了,但是就是因为在写cmp()函数的时候,把return a>b写成return a>=b,结果就超时了,去掉等号后,就成了16MS,无语,看到好多都是0MS,看来自己的算法还要再改进
POJ上AC的代码:
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
int stick[65];
int book[65];
int n,sum,L,flag;
int cmp( int a, int b)
{
return a>b;
}
int dfs(int m, int left)
{
if(m==0 && left==0)
{
return 1;
}
if(left==0)
left=L;
if(left<stick[n-1])
return 0;
for(int i=0;i<n;i++)
{
if(book[i]==0 && stick[i]<=left)
{
book[i]=1;
if(dfs(m-1,left-stick[i]))
return 1;
else
{
book[i]=0;
if(left==L || stick[i]==left) //重要剪枝- --很重要否则会超时
return 0;
while(stick[i+1]==stick[i])
i++;
}
}
}
return 0;
}
int main()
{
while(~scanf("%d",&n) && n)
{
sum=0; flag=0;
for(int i=0;i<n;i++)
{
scanf("%d",&stick[i]);
book[i]=0;
sum+=stick[i];
}
sort(stick,stick+n,cmp);
for(L=stick[0];L<=sum/2;L++)
{
if(sum%L)
continue;
if(dfs(n,L))
{
printf("%d\n",L);
break;
}
}
if(L>sum/2)
{
printf("%d\n",sum);
}
}
return 0;
}