poj1011题解

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

using namespace std;

int *length, N;
bool *used;

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

//if (dfs(N, L, L, -1))

/*R为可供使用的小木棒还有多少个
 * M为累加到长度为L的木棒还需要多少长度
 * 一开始M肯定是为L的
 * 因为最开始的适合还没有添加木块,所以需要的长度为L
 * L当前需要的木棒长度
 * 最后一次拼接的木棒的下标
 * */
bool dfs(int R, int M, int L, int nLastStickNo) {
    //全部可用木棒都用完了
    //并且拼接为L长度的木棒需要的长度为0
    //说明已经拼接好了
    //则可以返回true
    if (R == 0 && M == 0)
        return true;

    //一根长度为L的木棒已经拼接好了
//    开始拼接下一根长度为L的木棒
//  更新M的值
//重新赋值为L
    if (M == 0)
        M = L;

    //当前需要的长度不等于L说明
    /*已经有木棒在参与构造了
     * 则将这个索引加1就可以了
     * else
     * 则是M等于L
     * 说明当前正在构造长度为L的木棒
     * 而当前还没有添加木棒上去
     * 所以索引应该从0开始遍历
     *
     * 好像这都像一个顺理成章的条件了
     * 并不像一个剪枝
     * 既然已经对木棒进行从大到小的排序
     * 并且是从最长的木棒开始遍历的长度
     * 那么不是就应该是从长选到短*/
    M != L ? nLastStickNo += 1 : nLastStickNo = 0;

    //从当前索引开始遍历到最大木棒数目
    for (int i = nLastStickNo; i < N; i++) {

        /*当前遍历木棒没有选择
         * 并且当前遍历的木棒的长度小于
         * 当前需要的木棒长度
         * 则将其选中
         * */
        if (used[i] == false && length[i] <= M) {

            /*如果当前遍历的小木棒不是第一根小木棒
             * 并且当前遍历的这跟小木棒前面的那根小木棒没有选择
             * 并且当前遍历的小木棒和它前面的那根小木棒长度相等
             * 则需要跳过当前遍历的小木棒
             * 进入下一根小木棒的选择
             * 直接continue*/
            if (i > 0 && used[i - 1] == false && length[i] == length[i - 1])
                continue;

            used[i] = true;

            /*选择当前遍历的木棒
             * 无法完成拼接
             * 即dfs结果返回false
             * 则需要取消当前木棒的选择
             * 即used[i]赋值为false*/
            if (dfs(R - 1, M - length[i], L, i))
                return true;
            else {
                used[i] = false;
                /*减法2
                 * 如果由于以后的拼接失败,需要重新调整当前棍子的拼法,
                 * 替换掉当前棍子中的第一根小木棒是不行的
                 *
                 * 减法3
                 * 如果当前这根长度未L的棍子拼接成功,但随后的棍子拼接失败,
                 * 仅替换已拼好棍子的最后一根小木棒是不行的*/
                if (M == L || length[i] == M)
                    return false;
            }
        }
    }
    return false;
}

int operation() {
    
    //将录入的小木棒长度从大到小排序
    sort(length, length + N, cmp);
    int nTotalLen = 0;
    //计算录入的小木棒的总长度
    for (int i = 0; i < N; i++)
        nTotalLen += length[i];

/*计算出木棒总和
 * 然后排序
 * 然后从最长木棒开始向木棒总和的一半开始遍历
 * */
    for (int L = length[0]; L <= nTotalLen / 2; L++) {
        if (nTotalLen % L == 0) {
//            初始化设置used数组为均为访问
//则全为false
            fill(used, used + N, false);
            if (dfs(N, L, L, -1))
                return L;
        }
    }

    //此时长度已经大于总长度的一半
    //所以此时最小的木棒长度就是全部木棒的和
    return nTotalLen;
}

int main() {
    while (scanf("%d", &N)) {
        if (N <= 0)
            break;
        length = new int[N];
        used = new bool[N];
        for (int i = 0; i < N; i++)
            scanf("%d", &length[i]);
//        打印最短木棒的长度
        printf("%d\n", operation());
        delete[] used;
        delete[] length;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值