POJ1011 Sticks

经典的DFS剪枝,值得回味。
多个剪枝:
1.木棍长度L必须整除总长sum;
2.木棍总长L大于等于小木棍最长那个,小于等于sum;
3.小木棍长度排序,从大的开始枚举,因为大的约束多,所以能接近根处剪枝;
4.小木棍排序过,同样长度的相邻,如果有个同样长度的不行,那么之后同样长度的也不行;
5.最重要有效的剪枝:假设木棍长度是L,那么现在是一部分L拼完了,一部分L没拼完如下(\分割):
L, L, L, L \ L, L, L
那么在拼\后面第一个L时,A[j]不行,那么由于后面三个L都一样长,是等效的,所以A[j]对后面三个L都不行,就可以直接返回false了。
那么如果这是\后面是L',也就是已经拼了一些,那么就和后面不等效,不能剪枝。
L有可能如图出现在第五个而不一定是第一个,是可能因为之前拼了一些后造成的约束条件才导致A[j]在第五个L失败。

#include <iostream>

#include <string>

#include <algorithm>



using namespace std;



bool cmp(int a, int b) {

    return  a > b;

}



// leftLength: 本段木棍还需要拼的长度

// targetLength: 每段木棍的目标长度

// leftCount: 还需要拼出来的木棍

bool sub(int leftLength, int targetLength, int leftCount, int* stick, bool* visit, int n) {

    if (leftCount == 0) return true;

    if (leftLength == 0) return sub(targetLength, targetLength, leftCount - 1, stick, visit, n); // 拼出了一根木棍,拼下一根

    for (int i = 0; i < n; i++) {

        if (visit[i] || stick[i] > leftLength)

            continue;

        if (i > 0 && !visit[i - 1] && stick[i - 1] == stick[i]) continue; // 如果上一根小棍子长度一样且无法拼出,那么

        visit[i] = true;

        if (sub(leftLength - stick[i], targetLength, leftCount, stick, visit, n)) {

            return true;

        }

        visit[i] = false;

        if (leftLength == targetLength) {

            return false; // 最有效的剪枝,如果拼L时,A[j]不行,那么后面的未拼的也都是L,等价,所以A[j]永远不行

        }

    }

    return false;

}



int main(void) {

    int n;

    while(cin >> n && n)  

    {

        bool found = false;

        int sum = 0;

        int* stick = new int[n];

        bool* visit = new bool[n];

        for (int i = 0; i < n; i++) {

            cin >> stick[i];

            sum += stick[i];

            visit[i] = false;

        }

        sort(stick, stick + n, cmp); // 排序,从最长的棍子开始拼,因为越长的棍子约束越大,从最接近根处剪枝

        int maxLen = stick[0];

        for (int l = maxLen; l <= (sum + 1) / 2; l++) { // 只枚举到sum/2即可,如果还不行,那就是sum了

            if (sum % l == 0) { // 必须要能整除

                if (sub(l, l, sum / l, stick, visit, n)) {

                    found = true;

                    cout << l << endl;

                    break;

                }

            }

        }

        // sum

        if (!found) cout << sum << endl;

    }

    return 0;

}

  

你可能感兴趣的:(poj)