hdu 1455/poj 1011 Sticks(DFS剪枝神题)

Sticks

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9455    Accepted Submission(s): 2787


Problem Description
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. 
 

Input
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.
 

Output
The output file contains the smallest possible length of original sticks, one per line. 
 

Sample Input
  
  
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
 

Sample Output
  
  
6 5
题意:地上有n根长度不一定相等的木棍,现在要你把这些木棍拼起来,使得所有木棍长度一样,求这个最小的长度

思路:注意一定有解,最起码为所有木棍之和(都拼成一根)

然后我们就可以从这些木棍里面最大的木棍长度开始枚举到木棍之和,第一个满足条件的即是我们要求的。

注意这里要从大到小排序,我们在取木棍的时候优先取大的木棍有利于更快剪枝。

注意要有足够强的剪枝才可以过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;
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值