Hoj 1049 Sticks

本题是经典DFS+剪枝题。

题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=1049

但是裸DFS我都写了一段时间,必然是TLE的,但是是一种很好的搜索思路,贴裸DFS代码:

#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;


int array[10000];
int used[10000];

bool cmp(int a,int b)
{
    return a>b;
}
int sum;
int len;
int piece_num;
int n;
//cur-当前拼接长度,poi-当前棍索引
//fin-已完成的原棍数
bool dfs(int cur,int poi,int fin)
{
    if(cur == len)
    {
        fin++;
        cur = 0;
        poi = 1;
    }
    if(fin == piece_num)
    {
        return true;
    }
    for(int i=poi;i<n;i++)
    {
        if(used[i] == 0)
        {
            if(cur + array[i]<=len)
            {
                used[i] = 1;
                if(dfs(cur+array[i],i+1,fin))
                {
                    return true;
                }
                used[i] = 0;
            }
        }
    }
    return false;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif

    int max;
    while(scanf("%d",&n)!=EOF && n!=0)
    {
        sum = 0;
        for(int i=0;i<n;i++)
        {
            scanf(" %d",&array[i]);
            sum += array[i];
        }
        sort(array,array+n,cmp);
        memset(used,0,sizeof(used));
        max = array[0];
        for(len = max;len<=sum;len++)
        {
            if(sum%len == 0)
            {
                piece_num = sum/len;
                if(dfs(0,0,0))
                {
                    printf("%d\n",len);
                    break;
                }
            }
        }
    }
    return 0;
}

然后加剪枝优化搜索,共加了四个剪枝,但是前两个剪枝已经足够将时间压到0S。AC掉

#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;


int array[64];
int used[64];
int next[64];

bool cmp(int a,int b)
{
    return a>b;
}
int sum;
int len;
int piece_num;
int n;
int tail;

//指向下一个不同与此棍长度的索引
void countNext()
{
    memset(next,-1,sizeof(next));
    int i = 0;
    int c = 0;
    while(i<n)
    {
        c = 0;
        for(int j=i+1; j<n; j++)
        {
            if(array[i]!=array[j])
            {
                c = j;
                break;
            }
        }
        if(c == 0)
        {
            c = n;
        }
        for(int j=i; j<c; j++)
        {
            next[j] = c;
        }
        i = c;
    }
}
//求出刚好能足够拼出一个木棍原型长度的标号
void countTail()
{
    int s = 0;
    for(int i=n-1;i>=n;i++)
    {
        s += array[i];
        if(s>=len)
        {
            break;
        }
    }
    tail = s;
}
int countRes(int m)
{
    //计算下标大于等于m且未使用的小棒总长之和
    int sumr=0;
    for (int i=m;i<n;i++)
    {
        if (used[i]==false)
        {
            sumr += array[i];
        }
    }
    return sumr;
}
//cur-当前拼接长度,poi-当前棍索引
//fin-已完成的原棍数
bool dfs(int cur,int poi,int fin)
{
    if(cur == len)
    {
        fin++;
        cur = 0;
        poi = 1;
    }
    if(fin == piece_num)
    {
        return true;
    }
    int i = poi;
    while(i<n)
    {
        if(used[i] == 0)
        {
            if(cur + array[i]<=len)
            {
                used[i] = 1;
                if(dfs(cur+array[i],i+1,fin))
                {
                    return true;
                }
                used[i] = 0;
                //剪枝2:如果当前cur = 0不成立,那么此len无效,不用再验证以后
                if(cur == 0)
                {
                    return false;
                }
                //剪枝3:若尝试一根小棒恰能组合出当前原棒的剩余长度的小棒失败,则不再尝试比它更小的小棒
                if(cur+array[i] == len)
                {
                    return false;
                }
            }
            //剪枝1:长度相同的棍只测验一次
            i = next[i];
            //剪枝4:计算当前所剩可用小棒是否足以拼出当前这支木棒原型
            if(i>=tail && countRes(i)<len - cur)
            {
                return false;
            }
            continue;
        }
        i++;
    }
    return false;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif

    int max;
    while(scanf("%d",&n)!=EOF && n!=0)
    {
        sum = 0;
        for(int i=0; i<n; i++)
        {
            scanf(" %d",&array[i]);
            sum += array[i];
        }
        sort(array,array+n,cmp);
        max = array[0];
        countNext();
        for(len = max; len<=sum; len++)
        {
            if(sum%len == 0)
            {
                memset(used,0,sizeof(used));
                countTail();
                piece_num = sum/len;
                if(dfs(0,0,0))
                {
                    printf("%d\n",len);
                    break;
                }
            }
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值