广度优先搜索的分酒问题解决方案

本文不再叙述具体的分酒问题,直接推广至n个容器的分酒问题解决方案。

首先,我们看看dWAlgorithm.h中的构成。
typedef vector<int> POINTS;
typedef vector<POINTS> PATHS;
typedef vector<PATHS> TREES;
给vector容器起别名,后面都是使用这三个,请注意。
PATHS operatorProduce(int, bool),该函数是产生操作算符,也就是液体转移的算符。对于n个容器的情形,这个函数最多能产生n^2个算符。
PATHS targetProduce(POINTS, const POINTS &),这个函数输出一个目标状态集合,设计这个的主要目的是用于处理一些特殊的情形。
例如,我们需要把酒分成(4,3,2),但我们不关心4,3,2的位置,或者说在哪个容器内,那么可能的目标状态就包含(4,2,3)、(2,3,4)、(2,4,3)、
(3,4,2)、(3,2,4)这剩下的5个。
POINTS operate(POINTS, const POINTS &, const POINTS &),这个函数是使用操作算符对状态进行转移的函数,同时,操作的结果是受容器容积限制的。
bool checkTP(const POINTS &, const PATHS &)这个函数是检查一个状态是否存在于一个状态集合中。
bool checkLimite(const POINTS &, const POINTS &),该函数用于判断状态是否超出容器限制,operate函数使用了该函数。
int sumP(const POINTS &),计算某一状态的酒水总量。
PATHS statusGenerate(const POINTS &, const int, const int),该函数用于产生酒水总数在一定范围内的状态集合。设计这个函数的主要目的是为了找
出最大的步数,通常的分酒问题是不需要该函数的。
PATHS opG(int, bool),是为提高statusGenerate函数效率而设计的,opS是用于产生状态的。
最后说一下分酒主函数。
bool divideWinePath(const POINTS &, const POINTS &, const POINTS &, TREES &, PATHS &, bool );
首先,广度优先搜索就是完整扩展了一层结点之后,才会继续扩展下一层结点,这和深度优先是不同的。
如果直接应用广度优先搜索,并且不及时剪除掉一些无用的枝,会导致数据量过大。笔者在没有考虑剪枝策略的情况下,分酒问题进行了求解,但是对于4
容器,步数在13以上的路径,其内存占用量能达到15GB,这对于实际的问题解决是不好的。所以,为了优化算法,必须除去一部分的结点来减少内存占用。
这里采用的剪枝策略为,如果在之前的所有节点中存在和刚扩展的节点一样的节点,那么就将该节点去除。因为我们需要找的是最短的路径,如果刚扩展的
节点在之前出现过,那么这个节点所在路径肯定不是最短的路径。
如果直接加入这个剪枝策略,效果并不是很理想,因为,随着树的扩张,节点数量也是快速增长的,那么当层数达到一定时,就需要花费大量的时间去对比
之前的节点。然而,通过产生的状态集,我们知道,和大量的节点数相比,状态集的容量是很小的。于是,我们可以用一个状态集合来表示之前出现过的节
点。当出现了新的节点,并且在状态集合中没有出现过,那么就把该节点加入到这个状态集合中。这样,我们就通过一个小容量的集合来取代了大容量的树
。这样就解决了函数运行到后期效率过低的问题。
divideWinePath函数的参数依次为初始状态,容量限制,初始目标状态,输出结果树,输出有效状态集合,目标产生选项(true表示多目标,false表示单目

 

标)。返回值为true表示存在最小路径,false表示不存在路径。

 

 

dWAlgorithm.h

#ifndef DWALGORITHM_H_INCLUDED
#define DWALGORITHM_H_INCLUDED

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

namespace divideWine
{
using std::vector;
using std::cout;
using std::cin;
using std::endl;

typedef vector<int> POINTS;
typedef vector<POINTS> PATHS;
typedef vector<PATHS> TREES;

PATHS operatorProduce(int, bool);
PATHS targetProduce(POINTS, const POINTS &);
POINTS operate(POINTS, const POINTS &, const POINTS &);

bool checkTP(const POINTS &, const PATHS &);

bool checkLimite(const POINTS &, const POINTS &);

int sumP(const POINTS &);

PATHS statusGenerate(const POINTS &, const int, const int);
PATHS startStatusGenerate(const POINTS &);
PATHS selectStartStatus(const PATHS &, int);
POINTS binary2Decimal(int, int);
POINTS myDot(const POINTS &, const POINTS &);

PATHS opG(int, bool);
POINTS opS(const POINTS &, const POINTS &);

bool judge(int, int, int);
bool _judge(bool, int, int, int);
//divideWinePath是主接口,参数依次为开始状态,各瓶容量(限制),目标状态,结果,这个忘记了,最后一个bool值是为了切换函数的运行模式,True是一次搜索多个目标,为了找出当前容量限制下的最大搜索长度,False就是一般的搜索形式,即按照给定的target去搜索。函数会返回一个bool值来提示是否找到路径。
bool divideWinePath(const POINTS &, const POINTS &, const POINTS &, TREES &, PATHS &, bool );

}
#endif // DWALGORITHM_H_INCLUDED

 

 

dWAlgorithm.cpp

 

#include "dWAlgorithm.h"

using divideWine::PATHS;
using divideWine::POINTS;
//using divideWine::TREES;

PATHS divideWine::operatorProduce(int n, bool flag)
{
    PATHS operate;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            POINTS point;
            point.resize(n, 0);
            if (i != j)
            {
                point[i] = 1;
                point[j] = -1;
                operate.push_back(point);
            }
        }
    }
    if (flag)
    {
        for (int i = 0; i < n; i++)
        {
            POINTS point;
            point.resize(n, 0);
            point[i] = -1;
            operate.push_back(point);
        }
    }
    return operate;
}

PATHS divideWine::targetProduce(POINTS point, const POINTS &limite)
{
    PATHS targets;
    sort(point.begin(), point.end());
    while (next_permutation(point.begin(), point.end()))
    {
        if (checkLimite(point, limite))
            targets.push_back(point);
    }
    if (targets.empty() && checkLimite(point, limite))
        targets.push_back(point);
    return targets;
}

POINTS divideWine::operate(POINTS point, const POINTS &op, const POINTS &limite)
{
    int n = point.size();
    while (1)
    {
        for (int i = 0; i < n; i++)
            point[i] += op[i];
        if (!checkLimite(point, limite))
            break;
    }
    for (int i = 0; i < n; i++)
        point[i] -= op[i];
    return point;
}

bool divideWine::checkTP(const POINTS &point, const PATHS &path)
{
    int n = path.size();
    for (int i = 0; i < n; i++)
        if (path[i] == point)
            return true;
    return false;
}

bool divideWine::checkLimite(const POINTS &point, const POINTS &limite)
{
    int n = point.size();
    for (int i = 0; i < n; i++)
        if (point[i] > limite[i] || point[i] < 0)
            return false;
    return true;
}

int divideWine::sumP(const POINTS &point)
{
    int c = 0;
    int n = point.size();
    for (int i = 0; i < n; i++)
        c += point[i];
    return c;
}

PATHS divideWine::statusGenerate(const POINTS &limite, const int sumS, const int sumT)
{
    PATHS status, tempPath, path;
    int max_value = std::max(sumS, sumT);
    int min_value = std::min(sumS, sumT);
    POINTS startPoint;
    int k, h;
    bool flag = judge(min_value, max_value, sumP(limite));
    if (flag)
    {
        startPoint.resize(limite.size(), 0);
        k = sumP(startPoint);
        h = 1;
    }
    else
    {
        startPoint = limite;
        k = sumP(limite);
        h = -1;
    }
    path.push_back(startPoint);
    PATHS op = opG(limite.size(), flag);
    int N_1 = op.size();
    while (_judge(flag, min_value, max_value, k))
    {
        tempPath.clear();
        swap(tempPath, path);
        k += h;
        int tempN_1 = tempPath.size();
        for (int i = 0; i < tempN_1; i++)
        {
            for (int j = 0; j < N_1; j++)
            {
                POINTS tempPoint = opS(tempPath[i], op[j]);
                if (!checkLimite(tempPoint, limite))
                    continue;
                if (path.empty())
                {
                    path.push_back(tempPoint);
                    continue;
                }
                int position = find(path.begin(), path.end(), tempPoint) - path.begin();
                if (position == path.size())
                    path.push_back(tempPoint);
            }
        }
        if (k >= min_value && k <= max_value)
            status.insert(status.end(), path.begin(), path.end());
    }
    path.clear();
    tempPath.clear();
    return status;
}

PATHS divideWine::opG(int n, bool flag)
{
    PATHS path;
    int h;
    if (flag)
        h = 1;
    else
        h = -1;
    for (int i = 0; i < n; i++)
    {
        POINTS point;
        point.resize(n, 0);
        point[i] = h;
        path.push_back(point);
    }
    return path;
}

POINTS divideWine::opS(const POINTS &point, const POINTS &operators)
{
    POINTS p;
    int n = point.size();
    for (int i = 0; i < n; i++)
        p.push_back(point[i] + operators[i]);
    return p;
}

bool divideWine::judge(int min_value, int max_value, int sumLimite)
{
    if (max_value <= sumLimite - min_value)
        return true;
    else
        return false;
}

bool divideWine::_judge(bool flag, int min_value, int max_value, int k)
{
    if (flag)
        if (k < max_value)
            return true;
        else
            return false;
    else if (k > min_value)
        return true;
    else
        return false;
}

bool divideWine::divideWinePath(const POINTS &pointStart, const POINTS &limite, const POINTS &targetFirst, TREES &tree, PATHS &statusValid, bool targetFlag)
{
    tree.clear();
    statusValid.clear();
    TREES tempTree;
    PATHS targets, operators;
    PATHS test, tempStatus;
    test.push_back(pointStart);
    tree.push_back(test);
    test.clear();
    statusValid.push_back(pointStart);
    bool opFlag;
    if (sumP(pointStart) > sumP(targetFirst))
        opFlag = true;
    else if (sumP(pointStart) == sumP(targetFirst))
        opFlag = false;
    else
        return false;
    operators = operatorProduce(pointStart.size(), opFlag);
    if(targetFlag)
        targets = targetProduce(targetFirst, limite);
    else
        targets.push_back(targetFirst);
    if (pointStart == targetFirst)
        return true;
    auto n = operators.size();
    int flag = 0, _flag = 0;
    while (flag == 0 && tree.size() > 0)
    {
        tempTree.clear();
        swap(tempTree, tree);
        tempStatus.clear();
        auto tempN_1 = tempTree.size();
        for (int i = 0; i < tempN_1; i++)
        {
            PATHS tempPath = tempTree[i];
            for (int j = 0; j < n; j++)
            {
                POINTS tempPoint = operate(tempPath.back(), operators[j], limite);
                bool tempFlag = checkTP(tempPoint, targets);
                if (sumP(tempPoint) < sumP(targetFirst))
                    continue;
                if (tempFlag)
                {
                    _flag = 1;
                    flag = 1;
                }
                if (_flag == 1)
                {
                    _flag = 0;
                    PATHS _tempPath = tempPath;
                    _tempPath.push_back(tempPoint);
                    tree.push_back(_tempPath);

                    if (tempStatus.empty())
                    {
                        tempStatus.push_back(tempPoint);
                        continue;
                    }
                    auto position = find(tempStatus.begin(), tempStatus.end(), tempPoint) - tempStatus.begin();
                    if (position == tempStatus.size())
                        tempStatus.push_back(tempPoint);
                }
                else if (_flag == 0 && flag == 1)
                {
                    if (tempFlag)
                    {
                        PATHS _tempPath = tempPath;
                        _tempPath.push_back(tempPoint);
                        tree.push_back(_tempPath);

                        if (tempStatus.empty())
                        {
                            tempStatus.push_back(tempPoint);
                            continue;
                        }
                        auto position = find(tempStatus.begin(), tempStatus.end(), tempPoint) - tempStatus.begin();
                        if (position == tempStatus.size())
                            tempStatus.push_back(tempPoint);
                    }
                }
                else
                {
                    if (!checkTP(tempPoint, statusValid))
                    {
                        PATHS _tempPath = tempPath;
                        _tempPath.push_back(tempPoint);
                        tree.push_back(_tempPath);
                        if (tempStatus.empty())
                        {
                            tempStatus.push_back(tempPoint);
                            continue;
                        }
                        auto position = find(tempStatus.begin(), tempStatus.end(), tempPoint) - tempStatus.begin();
                        if (position == tempStatus.size())
                            tempStatus.push_back(tempPoint);
                    }
                }
            }
        }
        if (!tempStatus.empty())
            statusValid.insert(statusValid.end(), tempStatus.begin(), tempStatus.end());
    }
    tempStatus.clear();
    tempTree.clear();
    swap(tempTree, tree);
    auto tempN_2 = tempTree.size();
    for (int i = 0; i < tempN_2; i++)
        if (checkTP(tempTree[i].back(), targets))
            tree.push_back(tempTree[i]);
    if (tree.size() == 0)
        return false;
    tempTree.clear();
    return true;
}

一个调用的例子如下。
 

#include "dwalgorihm.h"
#include <iostream>
#include <string>

using namespace std;
using divideWine::PATHS;
using divideWine::POINTS;
using divideWine::TREES;

void printPoint(POINTS point);
void printPath(PATHS path);

int main()
{
    const POINTS start_status{ 0, 10, 0, 3 };//开始状态
    const POINTS limite{ 12, 10, 6, 3 };//容量限制
    const POINTS target{ 4, 1, 4, 3 };//目标状态
    TREES result;//结果
    PATHS p;//忘了是个啥了
    if (divideWine::divideWinePath(start_status, limite, target, result, p, false))
    //设定为False,明确函数的运行模式,具体内容在H文件中有写到。
    {
        cout << "From";
        printPoint(start_status);
        cout << " To ";
        printPoint(target);
        cout << " limite is:";
        printPoint(limite);
        cout << endl;
        cout << "Find " << result.size() << " method to devide the wine." << endl;
        for (int i = 0; i < result.size(); i++)
        {
            string str;
            if (i == 0)
                str = "st";
            else if (i == 1)
                str = "nd";
            else if (i == 2)
                str = "rd";
            else if (i >= 3)
                str = "th";
            cout << i + 1 << str << " method is:";
            printPath(result[i]);
        }

    }
    return 0;
}

void printPath(PATHS path)
//打印输出一个具体方法。
{
    for (int i = 0; i < path.size(); i++)
    {
        printPoint(path[i]);
        if (i < path.size() - 1)
            cout << "->";
        if (i == path.size() - 1)
            cout << endl;
    }
}
void printPoint(POINTS point)
//打印输出某一个点
{
    for (int j = 0; j < point.size(); j++)
    {
        if (j == 0)
            cout << "[";
        cout << point[j];
        if (j == point.size() - 1)
            cout << "]";
        else
            cout << ",";
    }
}

运行界面:

当时还写了一个基于Qt的GUI界面,有需要的可以拿去瞅瞅(代码找不见了,只有exe文件,不过逻辑代码是从这里搬的,有需要你们可以自己去写一个,谢谢大家。)

code和GUI都提供了百度网盘链接。

链接: https://pan.baidu.com/s/12bSvwAf_AVxhd9F1eGuYHA 提取码: t5bn

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值