Sticks POJ - 1011(枚举+dfs)

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
描述:这个题呢,就问一系列的木棍,能不能拼成一系列等长的木棍,使得操作过的木棍的长度尽量小。
思路:因为数量量不大,我们枚举长度,从小枚举,然后验证,是否存在这种拼法,当然枚举的长度需要整除所有木棍的总长,这是必要条件,然后枚举的端点必然是从原始最长的木棍开始,因为不可能比这个更短。
然后就搜索验证。
代码(超时):

import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;

public class Main 
{
    static int n;
    static int a[]=new int[66];
    static boolean sign;
    static int b[]=new int[3205];//存拼凑好的木棍
    static boolean vis[]=new boolean [66];
    static int num;
    static int len;
    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        for(;;)
        {
            n=sc.nextInt();
            if(n==0)
                break;
            int sum=0;
            int max=0;
            for(int i=0;i0,n);
            for(int i=max;iif(sum%i==0)//枚举
                {
                    sign=false;
                    len=i;
                    num=sum/i;
                    init();
                    dfs(0,0);
                    if(sign)
                        break;
                }
            }
            if(sign)
                System.out.println(len);
            else
                System.out.println(sum);

        }
    }
    static void init()
    {
        Arrays.fill(vis, false);
        Arrays.fill(b, 0);
    }
    static void dfs(int cur,int last)
    {
        if(cur==num)//说明存在这种拼法
        {
            sign=true;
            return;
        }
        for(int i=n-1;i>=0;i--)
        {
            if(vis[i])
                continue;
            if(last!=0&&a[i]>last)//选择的木棍要比上一放的木棍要断,减少冗余枚举
                continue;
            if(b[cur]+a[i]>len)//不符合
                continue;
            b[cur]+=a[i];
            vis[i]=true;
            if(b[cur]==len)//如果刚好放满,放下一个
                dfs(cur+1,0);
            else
                dfs(cur,a[i]);//没放满就继续尝试
            if(sign)//找到提前结束
                return;
            b[cur]-=a[i];
            vis[i]=false;
        }

    }
}

优化版本

#include
#include
#include
#include
#include
#define maxx 400005
using namespace std;
int a[100];
int n;
int len,cnt;
bool cmp(int x,int y)
{
    return x>y;
}
bool vis[100];
bool dfs(int num,int cap,int last)//当前需要处理第num个待拼凑木棍,现在的容量是cap,从第last个原来的木棍开始拼
{
    if(num==cnt+1)
        return true;//拼完了
    if(cap==len)
        return dfs(num+1,0,0);//第num个木棍拼凑完成
    int fail=0;
    for(int i=last;iif(!vis[i]&& cap+a[i]<=len &&fail!=a[i])
        {
            vis[i]=true;
            if(dfs(num,cap+a[i],i+1))
                return true;
            fail=a[i];//说明用a[i]的长度的木棍无法拼凑成功,下次遇到一样大小的跳过
            vis[i]=false;
            if(cap==0)//所有的尝试过了以后,当前的木棍都凑不出,以后的就更不可能,提前返回。
                return false;
        }
    }
    return false;//所有的尝试了
}
int main()
{
    for(;;)
    {
        cin>>n;
        if(!n)
            break;
        int sum=0;
        for(int i=0;iscanf("%d",a+i);
            sum+=a[i];
        }
        sort(a,a+n,cmp);//大到小枚举
        for(len=a[0];len<=sum;len++)
        {
            if(sum%len)
                continue;
            cnt=sum/len;
            memset(vis,0,sizeof(vis));
            if(dfs(1,0,0))
                break;
        }
        cout<return 0;
}

你可能感兴趣的:(DFS)