poj1011dfs+剪枝

Sticks
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 151961 Accepted: 36190

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 should 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

Source

[Submit]   [G

给n条木棍,拼成相同长度的木棍若干条最短长度为多少

只说说最难理解的剪枝

1 当新的棍子在后面dfs时候,没有找到一个可行解,那么这个棍子长度一定不符合

2 当棍子长度恰好能组成新棍子的长度,但是在后面搜索中并没有可行解,因此我们可以认为这个棍子长度不符合,这个我要说说我的理解,例如说棍子长度是7,还差5,我们还有 5 4 3 2 1根棍子,如果我们挑了5没有成立的解,那么我们挑(1,4)(2,3)那么更加不可能了,因为1,4对于全局来说是能更加自由地分配,如果更加自由分配的都不能组成这个棍子长度,那么以后一定不能组成这个长度。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
int vis[100];
int a[100];
int dfs(int len,int m,int s)//len要求长度,m为已经用的棍子数,s为当前这根棍子长度
{
    int i,j;
    if(m==n&&s==0) return 1;//成功找到一个符合的
    if(s==0) s=len;
    for(i=0; i<=n-1; i++)
    {
        if(vis[i]||a[i]>s) continue;
        vis[i]=1;
        if(dfs(len,m+1,s-a[i])) return 1;
        vis[i]=0;//消除标记
        if(a[i]==s||len==s) break;
        while(a[i]==a[i+1]) i++;
        //a[i]==s表示即使遇到刚好合适能组成len的棍子在后面的搜素都不能成功
        //那么继续搜索下去也是没有用的
        //len==s表明这一根新棍子在后面所有搜索情况都没有成功,直接剪枝
    }
    return 0;
}
int cmp(int a,int b)
{
    return a>b;
}
int main()
{
    while(scanf("%d",&n))
    {
        if(n==0) break;
        int i,M=0,sum=0,j;
        for(i=0; i<=n-1; i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        sort(a,a+n,cmp);
        for(i=a[0]; i<=sum/2; i++)
        {
            if(sum%i==0)
            {
                memset(vis,0,sizeof(vis));
                int k=dfs(i,0,i);
                if(k!=0)
                {
                    break;
                }
            }
        }
        if(i>sum/2)printf("%d\n",sum);
        else printf("%d\n",i);
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值