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;i<n;i++)
            {
                a[i]=sc.nextInt();
                max=Math.max(a[i],max);
                sum+=a[i];
            }
            Arrays.sort(a,0,n);
            for(int i=max;i<sum;i++)
            {
                if(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<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#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;i<n;i++)
    {
        if(!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;i<n;i++)
        {
            scanf("%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<<len<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值