Sticks
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 9455 Accepted Submission(s): 2787
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
6 5
思路:注意一定有解,最起码为所有木棍之和(都拼成一根)
然后我们就可以从这些木棍里面最大的木棍长度开始枚举到木棍之和,第一个满足条件的即是我们要求的。
注意这里要从大到小排序,我们在取木棍的时候优先取大的木棍有利于更快剪枝。
注意要有足够强的剪枝才可以过poj的这道题,具体可以看discuss里的大神说的:
1.所给出的小棍长度可能有重复,如果含有某一小棍的组合方案被推翻了,那么和它同样长的小棍也没必要尝试
2.为了减少重复的测试方案,比如测试了棍子1 2 3方案不合适,后面又测试了小棍子1 3 2 是没必要的。那么一定要按顺序开始测试,在当前小棍被加入到组合方案时,如还没有组成该目标长度,那么接下来要测试的小棍要从当前小棍的下一个开始测试。
3.要凑成某一目标长度的棍子,其中组成它的第一根小棍,怎么也无法和其他小棍凑出这个长度,那么没必要推翻这第一根小棍,直接推翻这个目标长度就可以了。
4.如果加上一个小棍x凑成了目标长度,但剩下的小棍再也凑不出这个长度了。那么没必要推翻这个小棍x,去测试其他的小棍,直接推翻这个目标长度就可以了。
特别是第三个和第四个方案,尤为重要。
有了这四个剪枝,这两个题就可以0ms过了
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 100
int a[N],f[N],flag,n;
bool cmp(int a,int b)
{
return a>b;
}
int dfs(int k,int len,int now,int num,int pos)
{
if(k==num)
return 1;
for(int i=pos+1; i<=n; i++)
{
if(f[i]) continue;
if(i>1&&!f[i-1]&&a[i]==a[i-1]) continue;
if(now+a[i]<len)
{
f[i]=1;
if(dfs(k,len,now+a[i],num,i)) return 1;
f[i]=0;
if(!now)
return 0;
}
else if(now+a[i]==len)
{
f[i]=1;
if(dfs(k+1,len,0,num,0)) return 1;
f[i]=0;
return 0;
}
}
return 0;
}
int main()
{
while(~scanf("%d",&n)&&n)
{
int sum=0;
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
sort(a+1,a+1+n,cmp);
int ans=sum;
for(int i=a[1]; i<=sum/2; i++)
{
if(sum%i!=0) continue;
memset(f,0,sizeof(f));
flag=0;
if(dfs(0,i,0,sum/i,0))
{
ans=i;
break;
}
}
printf("%d\n",ans);
}
return 0;
}
本文介绍了一个经典的编程问题——如何通过已切割成不同长度的小段木棍还原成原本相同长度的木棍,并给出了详细的解决思路与代码实现。
388

被折叠的 条评论
为什么被折叠?



