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;
}

  

原文地址:https://www.cnblogs.com/lautsie/p/3376949.html