EOJ1981 || POJ1011 经典dfs+剪枝+奇怪的数据

题目:EOJ1981 || POJ1011   经典dfs+剪枝+奇怪的数据

 

Description

George took sticks of the same length and cut them randomly until all partsbecame at most 50 units long. Now he wants to return sticks to the originalstate, but he forgot how many sticks he had originally

Input

The input contains blocks of 2 lines. The first line contains the number ofsticks parts after cutting, there are at most 64 sticks. The second linecontains the lengths of those parts separated by the space. The last line ofthe file contains zero.

Output

The output should contains the smallest possible length of original sticks, oneper line.

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

 

 

 

 

题目分析:

         给出n根小木棍求可以拼出的最短的相同长度的木棍长度。

思路为dfs递归枚举每根木棍,主要优化即剪枝技巧有:1.排序后由大到小枚举每根木棍,因为每根木棍都会被使用,优先选取大的可以减少递归深度。

2,待拼木棍长度为总长度的约数。

3,若有相同长度木棍在之前没有被选取,则之后不用再考虑此木棍。

4,若无法拼出长度<=SUM/2的小木棍,则只能拼出长度为sum的小木棍。

5,若当前剩余第一根木棍无法使用则说明无法完成任务可提前结束dfs,因为每根木棍都会被使用。

6,若待拼木棍长度为当前小木棍长度,则说明此木棍为最后一根小木棍。若在选取了当前这根小木棍还无法完成任务,则这根小木棍无法被使用,同样可提前结束程序。

做到以上几点剪枝就可以通过poj了,时间是16MS。但是却无法通过EOJ上5000ms的时限,参考网上限制了递归调用深度才能过,但想想感觉只是为过而过,没什么道理。

 

 

版本一:未加递归深度限制,可以通过poj1011

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

bool vis[65];

int a[65],n,sum,each;

bool cmp(const int &a,const int &b){

    return a>b;

}

 

bool dfs(int m,int left){

    if(m==0 &&left==0){

        return true;

    }

    if(!left)left=each;

    int i;

    for(i=0;i<n;++i){

        if(i>0&& a[i]==a[i-1] && !vis[i-1]) continue;

        if(vis[i]|| a[i]>left) continue;

        vis[i]=true;

        if(dfs(m-1,left-a[i]))

            returntrue;

        else{

            vis[i]=false;

            if(a[i]==left|| left==each)

                returnfalse;

        }

    }

    return false;

}

 

int main()

{

    while(scanf("%d",&n),n){

        sum=0;

        for(inti=0;i<n;++i){

            scanf("%d",&a[i]);

            sum+=a[i];

        }

        sort(a,a+n,cmp);

        int i;

        for(i=a[0];i<=sum/2;++i){

            if(sum%i)continue;

            memset(vis,false,sizeof(vis));

            each=i;

            if(dfs(n,each)){

                printf("%d\n",i);

                break;

            }

        }

        if(i>sum/2)printf("%d\n",sum);

    }

    return 0;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

版本二:加了递归调用次数,才能通过eoj

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

bool vis[65];

int a[65],n,sum,each,deep;

bool cmp(const int &a,const int&b){

   return a>b;

}

 

bool dfs(int m,int left){

   if(deep++>200000) return false;

   if(m==0 && left==0){

       return true;

    }

   if(!left) left=each;

   int i;

   for(i=0;i<n;++i){

       if(i>0 && a[i]==a[i-1] && !vis[i-1]) continue;

       if(vis[i] || a[i]>left) continue;

       vis[i]=true;

       if(dfs(m-1,left-a[i]))

           return true;

       else{

           vis[i]=false;

           if(a[i]==left || left==each)

                return false;

       }

    }

   return false;

}

 

int main()

{

   while(scanf("%d",&n),n){

       sum=0;

       for(int i=0;i<n;++i){

           scanf("%d",&a[i]);

           sum+=a[i];

       }

       sort(a,a+n,cmp);

       int i;

       for(i=a[0];i<=sum/2;++i){

           if(sum%i) continue;

           memset(vis,false,sizeof(vis));

           each=i;

           deep=0;

           if(dfs(n,each)){

                printf("%d\n",i);

                break;

           }

       }

       if(i>sum/2) printf("%d\n",sum);

    }

   return 0;

}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值