HDU 1455 Sticks(回溯,减枝很巧妙)

题目地址:点击打开链接

题意:小明拿来几根相同长度的棍子,然后把这些棍子截成好几节,问最后能拼成几根长度相同的棍子(要求这些棍子的长度最小)

思路:和HDU1518相似,可以看本博客,那道题解写的比较详细,这不过这道题得在那道题上多加几个减枝才能A

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cstring>
#include <climits>
#include <cmath>
#include <cctype>

using namespace std;

int a[70];
bool flag;
int n,l;
int length;
int visit[70];

void dfs(int start,int sum,int now)
{
    int i;
    if(flag)
        return;
    if(sum == l)
    {
        flag = true;
        return;
    }
    for(i=start; i<n; i++)
    {
        if(!visit[i])
        {
            if(now + a[i] == length)
            {
                visit[i] = 1;
                dfs(0,sum+1,0);
                visit[i] = 0;
            }
            else if(now + a[i] < length)
            {
                visit[i] = 1;
                dfs(i+1,sum,now+a[i]);
                visit[i] = 0;
                if(now == 0)//减枝1
                    return;
                while(a[i] == a[i+1])//减枝2,相同长度的就不用再搜了
                    i++;
            }
        }
    }
}

int main()
{
    int i;
    while(scanf("%d",&n) && n)
    {
        flag = false;
        int sum = 0;
        for(i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            sum += a[i];
        }
        sort(a,a+n);
        for(i=a[n-1]; i<=sum; i++)//最后组成的边肯定大于等于最长边,枚举边比较好,枚举条数不好,原因看超时代码下面的解释
        {
            if(sum % i == 0)
            {
                memset(visit,0,sizeof(visit));
                 l = sum  / i;
                 length = i;
                 dfs(0,0,0);
                 if(flag)
                 {
                     printf("%d\n",i);
                     break;
                 }
            }
        }
    }
    return 0;
}

因为这道题只求可能情况,不是求种类数,减枝方法还是挺多的(1)只要求可能情况,所以可以提前排序(由大到小或者由小到大都无所谓),每次只选它后面的,举个例子啊,1,6,2,5;一看边长就是7,从1搜到6,或者从6搜到1,都能说明这条边存在,所以只搜1到6就可以了,然后再看减枝1,这个特别巧妙,我再举个例子啊,边长为1,6,9,2,4,10;这个一看每条边就是16,减枝1之所以成立,是因为每条边都会用,所以当目前长度为0时选一个就行,而当长度为0时,选一个可能构不成边,例如这个例子,按选一个的思想,他会选1,4,6,显然是错误的,所以第一个没有组合情况,以后的都会有组合情况

超时代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cstring>
#include <climits>
#include <cmath>
#include <cctype>

using namespace std;

int a[70];
bool flag;
int n,l;
int length;
int visit[70];

void dfs(int start,int sum,int now)
{
    int i;
    if(flag)
        return;
    if(sum == l)
    {
        flag = true;
        return;
    }
    for(i=start; i<n; i++)
    {
        if(!visit[i])
        {
            visit[i] = 1;
            if(now + a[i] == length)
            {
                dfs(0,sum+1,0);
            }
            else if(now + a[i] < length)
            {
                dfs(i+1,sum,now+a[i]);
            }
            visit[i] = 0;
        }
    }
}

int main()
{
    int i,j;
    while(scanf("%d",&n) && n)
    {
        flag = false;
        int sum = 0;
        int min1 = 1000000000;
        for(i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            sum += a[i];
            if(a[i] < min1)
                min1 = a[i];
        }
        sort(a,a+n);
        for(i=sum; i>=min1; i--)
        {
            if(sum % i == 0)
            {
                memset(visit,0,sizeof(visit));
                l = i;
                length =  sum / i;
                for(j=0; j<n; j++)
                {
                    if(a[j] > length)
                        break;
                }
                if(j == n)
                {
                    dfs(0,0,0);
                    if(flag)
                    {
                        printf("%d\n",length);
                        break;
                    }
                }
            }
        }
    }
    return 0;
}


其实我这个代码写的还是比较逗的,最小值排序一下就能出来,我却特意求了一下,然后枚举的条数,结果里面多加了一个for循环减枝,并且for循环的范围大,枚举边的范围是从最大边到sum,而枚举条数是从最小边到sum,这样不就是把时间搞的更长了么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值