poj-1011

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

int tmpArray[300];

int sticks[64];
char stickUsed[64];
int maxLevel;

int comp( const void *a, const void * b)
{
return *(int *)a-*(int *)b;
}

int comp1( const void *a, const void * b)
{
return *(int *)b-*(int *)a;
}

void merge(int * array, int A1begin, int A1end, int B1begin, int B1end) {
    // printf("%d %d %d %d\n", A1begin, A1end, B1begin, B1end);
    int totalLength = A1end - A1begin + 1 + B1end - B1begin + 1;
    int insertPos = 0;
    int A1unInsertedPos = A1begin;
    int B1unInsertedPos = B1begin;

    while(1) {
        if (A1unInsertedPos > A1end || B1unInsertedPos > B1end) {
            break;
        }
        tmpArray[insertPos++] = array[A1unInsertedPos] < array[B1unInsertedPos] ?
                                    array[A1unInsertedPos++] : array[B1unInsertedPos++];
    }
    int leftDataPosBegin = A1unInsertedPos > A1end ? B1unInsertedPos : A1unInsertedPos;
    int leftDataPosEnd = A1unInsertedPos > A1end ? B1end : A1end;
    memcpy(tmpArray + insertPos, array + leftDataPosBegin, (leftDataPosEnd - leftDataPosBegin + 1)*sizeof(int));
    memcpy(array + A1begin, tmpArray, totalLength*sizeof(int));
}

void mergeSort(int * array, int begin, int end) {
    // printf("%d %d\n", begin, end);
    if (end <= begin) {
        return;
    } else {
        int middle = (begin + end)/2;
        mergeSort(array, begin, middle);
        mergeSort(array, middle+1, end);
        merge(array, begin, middle, middle+1, end);
    }
}

char dfs(int originallength, int length, int begin, int end) {
    if (length == 0) {
        int unused = -999;
        for (int i = 0; i <= end; i++) {
            if (!stickUsed[i]) {
                unused = i;
                break;
            }
        }
        if (unused == -999) {
            return 1;
        } else {
            stickUsed[unused] = 1;
            if (dfs(originallength, originallength - sticks[unused], unused + 1, end)) {
                return 1;
            } else {
                stickUsed[unused] = 0;
                return 0;
            }
        }
    } else {
        // printf("L %d\n", length);
        if (begin > end) {
            return 0;
        }
        
        if (length < 0) {
            // printf("return 0\n");
            return 0;
        }

        for (int i = begin; i <= end; i++) {
            if (!stickUsed[i]) {

                if ( i>0 && !stickUsed[i-1] && (sticks[i] == sticks[i-1])) {
                    continue;
                }

                int requiredLength = length - sticks[i];
                // printf("choose %d %d\n", sticks[i], requiredLength);
                if (requiredLength < 0) {
                    stickUsed[i] = 0;
                    continue;
                }
                stickUsed[i] = 1;

                char resulst = dfs(originallength, requiredLength, i + 1, end);
                if (resulst) {
                    // printf("return 1\n");
                    return 1;
                } else {
                    stickUsed[i] = 0;
                }
            }
        }
        // printf("return %d\n", res);
        return 0;
    }
}

void getShortest(int num, int sum) {
    // printf("%d %d\n", max, sum);
    // mergeSort(sticks, 0, num-1);
    qsort(sticks ,num, sizeof(int), comp1);
    // for (int i = 0; i <= num-1; i++) {
    //     printf("%d ", sticks[i]);
    // }
    // printf("\n");
    int max = sticks[0];
    for (int i = max; i <= sum/2; i++) {
        // printf("%d\n", i);
        if (sum % i == 0) { // a possible length,
            maxLevel = sum/i;
            if (dfs(i, i, 0, num-1)) {
                printf("%d\n", i);
                return;
            }
        }
    }
    printf("%d\n", sum);
}

void test() {
    // int array[] = {46, 78, 1, -99, 27, 356, 88, 1 ,7, 1, 89, 22, 10, 12, 7};
    int array[] = {46, 78, 1, -99, 27, 356, 88, 3 ,7, 9, 89, 22, 10, 12, 6};
    mergeSort(array, 0, sizeof(array)/sizeof(int) - 1);
    for (int i = 0; i < sizeof(array)/sizeof(int); i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}


int main() {
    // test();
    int num = 0;
    while(cin>>num) {
        memset(sticks, 0, sizeof(sticks));
        memset(stickUsed,0, sizeof(stickUsed));
        // printf("%d", num);
        if (!num) {
            return 0;
        }
        int sum = 0;
        for (int i = 0; i < num; i++) {
            scanf("%d", sticks + i);
            sum += sticks[i];
        }
        getShortest(num, sum);
    }
}

208K 16MS G++

深度树状搜索(也叫解答树), 丢了好久了, 一开始还和图的深度遍历搞混了,唉,要好好补补这一块的知识了,树状搜索用的地方其实挺多的.

在摸索这道题的过程中,慢慢的回想起来了深度树搜索的知识和步骤,当时做过一些最基本的题,比如从N个字符中列出所有不同的长度为M的字符组合之类的,

不过这一道题我按照最朴素的深度树搜索来,直接TLE了... :(

不过还是要列一下朴素深度树搜索的步骤,就当深化记忆:

首先,在搜索树的顶点,要确定一些状态值,来标示此状态的特殊性,题目中提供的是一个数组,本质上就是从数组中不断的选择数,看是否能够找到N个数,使其和

正好等于某个数L, 那么某一个状态下,便最起码有两个特殊的状态值:

1.在此状态下, 要找到的某几个数的和 S

2.当前数组被使用的情况(有些数已经在解答树的上几层被使用了,以后就不够再使用)。

对于第一个状态值,一个int变量即可,对于第二的状态值,一般有两种方式:

 1.在每一次做出选择的时候,将被选择的数与 当前数组 的第一个元素(原数组的第i个)互换位置,下一层要处理的数组是从原数组的

第i+1开始,这样就保证了,下一层解答使用的数组里面所有的元素都是之前没有被使用过的,而在该层的操作完成以后,要记住将此数组还原,

有点类似于函数调用完恢复上一层调用环境(本质上确实是一样的)。

那么,假设在某一层的可用数组有j个, 而要求的数的和是S ,那么就要遍历这j个数,对每个数k进行一次上面的替换,然后用剩下的j-1的数组,数和为S-A[k]

进行一次新的dfs, 如果某个数的dfs是成功的,那么就恢复环境返回成功,如果某个数dfs不成功,恢复环境,对数组的下一个数dfs,如果遍历完了数组没有

成功的,那么就返回失败。

2. 如果数组长度不是很长,那么可以用一个bitmap保存数组每个元素的使用情况,不用互换数组,每次找第一个没有被使用过的数.


针对这道题,有些特殊的设定:

因为是要求所有的stick都能组合为长度为L的stick,不能有一个stick最后被拉下,这就要求在某一轮dfs拼成功一个L以后,看看是否还有拉下的stick,

如果有,重置S为L,进行新一轮dfs,并返回此dfs的结果作为最终结果.

以上就是朴素解答树。

但是因为TLE的原因,不得不找更好的办法。

后来发现一种解法:

使用bitmap数组来记录数组的使用情况(最多64,可以接受),但是在搜索没有被使用的数的时候,不是从头开始,而是从上一轮选取的元素的位置开始,

直到找到能拼成功L的情况,在找到以后,才从数组头开始遍历,找到第一个没有被使用的数开始新一轮的一样的dfs,最后在拼成功并且没有

剩余可用数组元素的情况下返回成功, 这种思路可以极大的减少运算量(每次不是从头开始搜索遍历,而是从上一轮位置+1开始搜索遍历),并且

也能保证正确性,值得牢记。

还有些提速技巧

1 事先对数组从小到大排序,这样在后面遍历检测数组元素时,如果A[i]已经比L大,那么i到最后的数组元素必然不满足拼成的条件(都>L),返回失败.

 2 也是排序,在遍历数组元素时,如果发现某个元素值与前一个元素相等,而前一个元素也是没有被占用状态,这就意味着前一个元素的dfs失败的,那么因为该元素

与其相等,也必dfs失败,可以直接略过.


最后要说一下L是怎么得到的,因为对dfs解答树来说,必须有一个定长L才可以展开,这里其实就是从小到大枚举了,

题目要求求出最小的能拼完的L, 那么首先, L必定>=sticks中的最长的一个(max), 并且sticks的长度sum也必能整除L, 而且L的最大值就是sum

这样,就可以搞一个从max到sum/2(超过sum/2 就必是sum了)的遍历 i,如果 sum能整除i, 那么就可能是满足条件的,以i作为L进行一轮dfs, 如果成功

输出i,否则继续,因为是从小到大,就保证了,最终解必是最小的。

很多题其实都是这种枚举然后dfs的步骤, 枚举难在找到合适的上下限,dfs难在实现和效率.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值