深度优先搜索:拯救少林神棍

原文地址:https://www.jianshu.com/p/68a1dc82a96b

问题描述

据说,少林寺的镇寺之宝,是救秦王李世民的十三棍僧留下的若干根一样长的棍子。在民国某年,少林寺被军阀炮轰,这些棍子被炸成\(N\)节长度各异的小木棒。战火过后,少林方丈想要用这些木棒拼回原来的棍子。可他记不得原来到底有几根棍子了,只知道古人比较矮,且为了携带方便,棍子一定比较短。他想知道这些棍子最短可能有多短。
输入:\(N\)节木棒的长度。
输出:能拼成的最小的棍子长度。

图1 问题描述

解题思路

图2 解题思路1
图3 解题思路2
图4 解题思路3
图5 解题思路4
图6 解题思路5
图7 解题思路6

剪枝方案

通过剪枝来提高速度。

图8 剪枝方案1
图9 剪枝方案2
图10 剪枝方案3
图11 剪枝方案4
图12 剪枝方案5
图13 剪枝方案6
图14 剪枝方案7
图15 剪枝方案8

程序实现

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>

using namespace std;

int N;//木棒总数目
int L;//棍子长度
const int MAXN = 101;
vector<int> stickLength;//存放各木棒长度
bool used[MAXN];//标记各木棒是否使用过
int totalLen;
int lastStickID;


bool DFS(int nLeftSticks, int nNeededLength)
{
    if(nLeftSticks == 0 && nNeededLength == 0) return true;
    if(nNeededLength == 0) nNeededLength = L;
    //剪枝4:确保拼的部分从长到短排序
    int nowStickID = 0;
    if(nNeededLength < L)
        nowStickID = lastStickID + 1;
    for(int i = nowStickID; i < N; i++)
        if((!used[i]) && stickLength.at(i) <= nNeededLength)
            {
                if(i > 0) //剪枝1:不要在同一个位置多次尝试相同长度的木棒
                    {
                        if((!used[i - 1]) && stickLength.at(i - 1) == stickLength.at(i))
                            continue;
                    }
                used[i] = true;
                lastStickID = i;
                if(DFS(nLeftSticks - 1, nNeededLength - stickLength.at(i)))
                    return true;
                else
                    {
                        used[i] = false;//下次可能会用到该木棒
                        if(nNeededLength == L || nNeededLength - stickLength.at(i) == 0) return false;
                        //剪枝2:一根棍子的第一个木棒不行就不要再尝试,
                        //因为该木棒终将出现在另一根棍子中,也拼不出来
                        //剪枝3:不要希望通过仅仅替换已经拼好棍子的最后一根木棒来拼成功
                        //因为该木棒终将出现在另一根棍子中,将其与替换其的短木棒组合进行交换也能成功拼成另一根棍子,矛盾
                    }
            }
    return false;
}


int main()
{
    while(1)
        {
            cin >> N;
            if(N == 0)
                break;
            int tmpLen;
            memset(used, 0, sizeof(used));
            stickLength.clear();
            totalLen = 0;
            for(int i = 0; i < N; i++)
                {
                    cin >> tmpLen;
                    stickLength.push_back(tmpLen);
                    totalLen += tmpLen;
                }
            sort(stickLength.begin(), stickLength.end(), greater<int>());
            for(L = stickLength.at(0); L <= totalLen / 2; L++)
                {
                    if(totalLen % L)
                        continue;
                    if(DFS(N, L))
                        {
                            cout << L << endl;
                            break;
                        }
                }
            if(L > totalLen / 2) cout << totalLen << endl;
        }
    return 0;
}

运行结果

图16 运行结果

猜你喜欢

转载自www.cnblogs.com/cherrychenlee/p/10889452.html