一.题目链接:

POJ-1011

二.题目大意:

给你 n 个小木棍,要求将其中不超过 50 长度的木棍拼成若干个木棒.

并且使木棒的长度均相同,最后输出木棒的最小长度.

三.分析:

这道 dfs 剪枝快剪成  了。。。

①:确定答案的的范围:答案必定比最长木棍长 且 必定为不超过 50 木棍长度和的约数.

②:优化搜索顺序.

③:vis 标记去重 + 最优性剪枝 + 排除等效冗余剪枝.

④:空木棒的等效剪枝 + 贪心剪枝.

详见代码.

四.代码实现:

#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-8
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
using namespace std;

const int M = (int)1e2;
const int mod = 99991;
const int inf = 0x3f3f3f3f;

int a[M + 5];
bool vis[M + 5];
int n, m, x, sum, len, cnt;

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

bool dfs(int num, int have, int last)
{
    if(num > cnt)
        return 1;
    if(have == len)
        return dfs(num + 1, 0, 1);
    int fail = 0;
    for(int i = last; i <= n; ++i)
    {
        if(!vis[i] && have + a[i] <= len && fail != a[i])
        {
            vis[i] = 1;
            if(dfs(num, have + a[i], i + 1))
                return 1;
            vis[i] = 0;
            fail = a[i];
            if(!have || have + a[i] == len)
                return 0;
        }
    }
    return 0;
}

int main()
{
    while(~scanf("%d", &n) && n)
    {
        m = sum = len = 0;
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", &x);
            if(x > 50)  continue;
            a[++m] = x;
            sum += x;
            len = max(len, x);
        }
        n = m;
        sort(a + 1, a + n + 1, cmp);
        for( ; ; ++len)
        {
            if(sum % len)   continue;
            cnt = sum / len;
            memset(vis, 0, sizeof(vis));
            if(dfs(1, 0, 1))   break;
        } 
        printf("%d\n", len);
    }
    return 0;
}