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

      

  • 相关阅读:
    配置samba
    extern c
    剑指offer 孩子们的游戏
    剑指offer 扑克牌顺子
    剑指offer 翻转单词顺序列
    剑指offer 左旋转字符串
    mysql查看或显示当前存在多少数据库
    vim替换
    平衡二叉树
    将employees表的所有员工的last_name和first_name拼接起来作为Name,中间以一个空格区分
  • 原文地址:https://www.cnblogs.com/lautsie/p/3376949.html
Copyright © 2020-2023  润新知