POJ 1011 Sticks(搜索问题)

44 篇文章 3 订阅
11 篇文章 0 订阅

题目链接
http://bailian.openjudge.cn/practice/1011/

思路一(TLE):
输入数据时,计算出需要枚举的原始棍子长度的上界下界,然后枚举。对每一个stick,将他归到某一组棍子中(如果当前枚举的原始棍子长度为i,一组棍子的总和就应该恰好是i),然后搜索。

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

int N, flag;
int stick[68];
int group[64];      // 每一分组暂时的和

bool cmp(int a, int b)
{
    return a>b;
}

void f(int index, int g_num, int max_num)
{
    // 判断是否超出每一组最大值
    int judge = 1;
    for(int i=0;i<g_num;i++)
    {
        if(group[i] != max_num)
        {
            judge = 0;
            break;
        }
    }
    if(judge == 1)      // 已经确定这样分组可行
    {
        flag = 1;
        return;
    }


    // 对元素i,枚举它属于的组数
    for(int i=0;i<g_num;i++)
    {
        if(group[i]+stick[index] <= max_num)
        {
            group[i] += stick[index];
            f(index+1, g_num, max_num);
            group[i] -= stick[index];
        }
    }
}

int main()
{
    while(scanf("%d", &N) && N>0)
    {
        int tot = 0, max_ele = -9999;   // 枚举的范围
        // 输入stick的值
        for(int i=0;i<N;i++)
        {
            scanf("%d", &stick[i]);
            max_ele = max(max_ele, stick[i]);
            tot += stick[i];
        }

        sort(stick, stick+N, cmp);

        // 最终应该有 tot/i 组
        for(int i=max_ele;i<=tot;i++)
        {
            flag = 0;       // 标记这样分配是否可以
            if(tot%i != 0)
                continue;
            for(int j=0;i<tot/i;j++)
                group[j] = 0;
            f(0, tot/i, i);

            if(flag == 1)
            {
                printf("%d\n", i);
                break;
            }
        }

    }
    return 0;
}

思路2:使用当前的棍子,不断拼接成长度为len的原始棍子。(len的值用枚举获得)由大到小排序,因为更长的棍子对其他棍子约束力更大。对于两个长度相同的棍子,如果前一个棍子匹配失败,后一个棍子不用匹配,即可剪枝,加快速度。

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

int N, flag;
int stick[68];
int vis[68];

bool cmp(int a, int b)
{
    return a>b;
}

void dfs(int tot_used, int now_len, int now_stick, int each_len)
{
    if(flag == 1)
        return;
    if(now_len == 0)        // 找到下一个开头的小棒
    {
        for(int i=0;i<N;i++)
        {
            if(vis[i] == 0)
            {
                vis[i] = 1;
                dfs(tot_used+1, stick[i], i+1, each_len);
                vis[i] = 0;
                return;
            }
        }
    }

    if(now_len == each_len)     // 又拼好一根
    {
        if(tot_used == N)
        {
            flag = 1;
            return;
        }
        dfs(tot_used, 0, 0, each_len);      // 再重新开始拼接
        return;
    }

    for(int i=now_stick;i<N;i++)
    {
        if(vis[i]==0 && now_len + stick[i] <= each_len)
        {
           if(vis[i-1]==0 && stick[i] == stick[i-1])
            continue;      //  不重复搜索:最重要的剪枝。前一根和他一样的,拼不起来,那它也不用试了
           vis[i] = 1;
           dfs(tot_used+1, now_len + stick[i], i + 1, each_len);
           vis[i] = 0;
       }
    }
}

int main()
{
    while(scanf("%d", &N) && N>0)
    {
        int tot = 0, max_ele = -9999;   // 枚举的范围
        // 输入stick的值
        for(int i=0;i<N;i++)
        {
            scanf("%d", &stick[i]);
            max_ele = max(max_ele, stick[i]);
            tot += stick[i];
            vis[i] = 0;
        }

        sort(stick, stick+N, cmp);      // 降序排序

        // 最终应该有 tot/i 组
        for(int i=max_ele;i<=tot;i++)
        {
            flag = 0;       // 标记这样分配是否可以
            if(tot%i != 0)
                continue;
            //f(0, tot/i, i);
            dfs(0, 0, 0, i);

            if(flag == 1)
            {
                printf("%d\n", i);
                break;
            }
        }

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值