深搜+小孩分游问题

1.问题描述

小孩分油问题

两个小孩去打油,一人带了一个一斤的空瓶,另一个带了一个七两、一个三两的空瓶。原计划各打一斤油,可是由于所带的钱不够,只好两人合打了一斤油,在回家的路上,两人想平分这一斤油,可是又没有其它工具。试仅用三个瓶子(一斤、七两、三两)精确地分出两个半斤油来。

2.算法设计

令状态R、E、S分别表示十两、七两和三两的瓶子中所装的油量。如问题所述,初始时有(R,E,S)=(10,0,0),问题要求即仅通过这三个瓶子,将油量状态转变为(R,E,S)=(5,5,0)。

该问题较为特殊,我们发现七两的瓶子和三两的瓶子所能装的油的总量恰好为十两。因此,我们可以将十两的瓶子等同于一个无穷大的油桶,任何时候七两和三两的瓶子都可以通过这个油桶装满或倒空油。在这个假设下,原问题即被简化为:初始状态(E、S)=(0,0),要求仅通过这两个瓶子,将状态转变为(E、S)=(5,0)。在这个目标状态下,十两的瓶子中自然装了五两油。

将两个瓶子的状态转变以及对应的规则列表如下:


规则号


规则


解释


1


(E,S) and E<7 → (7,S)


7两瓶不满时装满


2


(E,S) and S<3 → (E,3)


3两瓶不满时装满


3


(E,S) and E>0 → (0,S)


7两瓶不空时倒空


4


(E,S) and S>0 → (E,0)


3两瓶不空时倒空


5


(E,S) and E>0 and E+S≤3 → (0,E+S)


37两瓶中油全倒入3两瓶


6


(E,S) and S>0 and E+S≤7 → (E+S,0)


3两瓶中油全倒入7两瓶


7


(E,S) and S<3 and E+S≥3 → (E+S-3,3)


用7两瓶油装满3两瓶子


8


(E,S) and E<7 and E+S≥7 → (7,E+S-7)


用3两瓶油装满7两瓶子

在每个状态(E,S)下,我们均可以通过判断E、S的值来选择上述若干条规则进行状态转变。整个状态空间构成了一颗树,树根是初始状态(R,S)=(0,0),目标状态(R,S)=(5,0)则可能位于某些节点中。

因为该问题的状态空间较小,最多不超过(7*3=21)种状态,因此在实验中我们采用深度搜索的方法对问题进行求解。此外,为了避免对已经搜索过的状态重复搜索,程序中定义了一个数组,用于存储已经搜索过的状态,仅有当当前状态没有在该数组中出现过时,算法才对其进行搜索,并将该状态放入数组中。

3.程序流程

4.核心伪代码

function isVisited(E, S): 状态(E, S)是否搜索过,没有则将其入栈并标记已搜索。

初始状态(E, S) = (0, 0),并存入栈Stack

while 栈Stack不为空:

取出栈顶元素(E, S),并输出

If (E, S) == (5, 0), then

分油成功,break;

if E < 7, then:

(E, S) = (7, S), isVisited(E, S)

if E < 3, then:

(E, S) = (E, 3), isVisited(E, S)

if E > 0, then:

(E, S) = (0, S), isVisited(E, S)

if S > 0, then:

(E, S) = (E, 0), isVisited(E, S)

if E > 0 and E+S <= 3, then:

(E, S) = (0, E+S), isVisited(E, S)

if S > 0 and E+S <= 7, then:

(E, S) = (E+S, 0), isVisited(E, S)

if S < 3 and E+S >= 3, then:

(E, S) = (7, S), isVisited(E+S-3, 3)

if E < 7 and E+S >= 7, then:

(E, S) = (7, S), isVisited(7, E+S-7)

end

5.代码运行及测试

算法运行结果如下所示,经过10次操作后,准确得将油划分为两个五两。

6.结论

本实验是对状态空间采用深搜的方法实现的。程序中设置了辅助数组用于保存已经搜索过的状态,且该问题的状态空间很小,因此深搜不会出现无穷解的情况。只要目标状态设置合理且存在,深搜一定能在有限的步骤里求得。但是在小孩分油问题中,深搜所得结果不一定为最优,广搜下得到的结果才是最优结果。但是由于深搜易于实现且速度快,因此实验中才选择深搜去实现。

本实验源码具有较强的扩展性,只要初始状态和目标状态设置合理,程序均可以成功将其状态转换过程输出。

7.源码

#include<iostream>
#include<stack>
#include<vector>
using namespace std;

struct State {
    int E; // 七两的瓶子
    int S; // 三两的瓶子 

    State(int E, int S) {
        this->E = E;
        this->S = S;
    }
};

//  深搜辅助栈
stack<State> Stack;

// 存储已经出现过的状态
vector<State> visited;

// 查询状态s先前是否出现过
bool isVisited(State s) {
    vector<State>::iterator it;
    for (it = visited.begin(); it != visited.end(); it++) {
        if (it->E == s.E && it->S == s.S) return true;
    }
    return false;
}

// 倒油行为,状态转变
void move(State s) {
    // 查询当前状态先前是否访问过
    if (!isVisited(s)) {
        visited.push_back(s);
        Stack.push(s);
    }
}

int main() {
    int E = 0, S = 0;
    int fE = 5, fS = 0;
     cout<<"Please input the initial oil of bottles:"<<endl;
     cin>>E>>S;
     cout<<"Please input the final oil of bottles:"<<endl;
     cin>>fE>>fS;
    Stack.push(State(E, S));

    while(!Stack.empty()) {
        State cur = Stack.top(); Stack.pop();
        E = cur.E;
        S = cur.S;
        cout<<10 - E - S<<" "<<E<<" "<<S<<endl;

        // 到达目标状态
        if (E == fE && S == fS) {
            cout<<"Successfully reach the target state:("<<fE<<", "<<fS<<")!";
            return 0;
        }

        // 将七两的瓶子装满
        if (E < 7) move(State(7, S));
        // 将三两的瓶子装满
        if (S < 3) move(State(E, 3));
        // 将七两的瓶子倒空
        if (E > 0) move(State(0, S));
        // 将三两的瓶子倒空
        if (S > 0) move(State(E, 0));
        // 将七两的瓶子全部装到三两的瓶子上
        if (E > 0 && E + S <= 3) move(State(0, E + S));
        // 将三两的瓶子全部装到七两的瓶子上
        if (S > 0 && E + S <= 7) move(State(E + S, 0));
        // 用七两的瓶子将三两的瓶子装满
        if (S < 3 && E + S >= 3) move(State(E + S - 3, 3));
        // 用三两的瓶子将七两的瓶子装满
        if (E < 7 && E + S >= 7) move(State(7, E + S - 7));
    }
    cout<<"Algorithm cannot find a solution!"<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/CSLaker/p/9875349.html

时间: 2024-10-20 01:38:03

深搜+小孩分游问题的相关文章

算法学习笔记 二叉树和图遍历—深搜 DFS 与广搜 BFS

图的深搜与广搜 马上又要秋招了,赶紧复习下基础知识.这里复习下二叉树.图的深搜与广搜.从图的遍历说起,图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其经典应用走迷宫.N皇后.二叉树遍历等.遍历即按某种顺序访问"图"中所有的节点,顺序分为: 深度优先(优先往深处走),用的数据结构是栈, 主要是递归实现: 广度优先(优先走最近的),用的数据结构是队列,主要是迭代实现: 对于深搜,由于递归往往可以方便的利

hdu 5313 Bipartite Graph 完全二分图 深搜 bitset应用

Bipartite Graph Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 577    Accepted Submission(s): 154 Problem Description Soda has a bipartite graph with n vertices and m undirected edges. Now he

算法-图是否为树(并查集或深搜)

今天做了一道很有意思的一道题,这道题虽然难度只是中等,但是里面涉及到的东西却是不少.其中,我在里面学习到了并查集这个东西,虽然不是很深刻,至少有一个印象:还有深搜,一直以来,深搜和广搜都是我的弱项,本文的理解是基于别人的博客:lintcode178. graph valid tree 图是否是树.我们来看看题 题意: 给出 n 个节点,标号分别从 0 到 n - 1 并且给出一个 无向 边的列表 (给出每 条边的两个顶点), 写一个函数去判断这张`无向`图是否是一棵树 样例: 给出n = 5 并

深搜+DP剪枝 codevs 1047 邮票面值设计

codevs 1047 邮票面值设计 1999年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1-MAX之间的每一个邮资值都能得到. 例如,N=3,K=2,如果面值分别为1分.4分,则在1分-6分之间的每一个邮资值都能得到(当然还有8分.9分和1

POJ1094 Sorting It All Out 拓扑排序(深搜)

题意:给定n和m,n表示大写字母的前n个,m表示m个关系对,求能否确定唯一排序. 分析:分三种情况 (1)当输入完当前的关系对之后,已经能确定矛盾(不需要完全输入m个关系对时) eg. 3 3       A<B       B<A         B<C       当输入到第二对已经能判断矛盾,此时记下当前的所需要用到的关系对数ans=2:       接着输入,但是后面的关系对不作处理 (2) 当输入完当前的关系对之后,已经能确定唯一排序(不需要完全输入m个关系对,    但是必须

BNU 1084 Expected Allowance (dp||母函数||深搜)

题目链接:http://www.bnuoj.com/v3/problem_show.php?pid=1084 题目大意:给你n个骰子,每个骰子有m个面,点数分别为(1-m),现在同时摇这n个骰子,(得到的点数和)-k  即为小明得到的钱数,当然小明每次最少会得到一元(即最后结果小于等于1时),问小明得到钱数的期望值. 解题思路:由于此题m*n较小,故按照普通的期望计算就行,即   摇到某个点数(i)的概率p*摇到的点数(i),其中显然(n=<  i  <=n*m)除以总的情况数(m的n次方)(

HDU 5305 Friends(深搜)

题意:t组数据,每组一个n和m表示点数和边数,接下来m条边,每条边两种状态,求每个点邻接边的两种状态数目相同的排列方式有几种 分析:从第一个顶点开始往下深搜每条边,每条边两种状态,注意回朔. 代码: #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int maxn = 10; int n,m,ans; int e

算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS

图的深搜与广搜 复习下二叉树.图的深搜与广搜. 从图的遍历说起.图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其经典应用走迷宫.N皇后.二叉树遍历等.遍历即按某种顺序訪问"图"中全部的节点,顺序分为: 深度优先(优先往深处走),用的数据结构是栈, 主要是递归实现. 广度优先(优先走近期的).用的数据结构是队列.主要是迭代实现. 对于深搜.因为递归往往能够方便的利用系统栈,不须要自己维护栈.所以通常实

dfs深搜

dfs深搜 什么是深搜? 百度百科:按照一定的顺序.规则,不断去试探,直到找到问题的解,试完了也没有找到解,那就是无解,试探时一定要试探完所有的情况(实际上就是穷举) 个人理解:暴力穷举每一种状态 它有什么好处? 容易理解 骗分利器 好写 它有什么弊端? 慢.毕竟是穷举每一种状态 一搜到底.对于递归次数非常多的话,dfs会非常浪费时间,甚至有可能会爆栈 如何实现? 算法流程图如下: #include <iostream> #include <cstdio> void dfs(int