三个水桶等分8升水

/**
 * -*- coding : GBK -*-
 * @Author : huangshengjiang
 * @Email : 1005542693@qq.com
 * @Date : 2016-10-28 14:00
 * @Last Modified Date : 2016-12-22 15:11
 * @FileName : 三个水桶等分8升水.cpp
 */

/*三个水桶等分8升水

    应用场景 : 有3个容器,分别是3升/5升/8升的水桶(没有刻度),其中容器8升的水桶装满水,3升和5升的水桶空的.要求将8升的水等分成两份,每份是4升水
               约束条件是只能用这3个容器,不能借用其他辅助容器. 要求列出所有等分成功的过程.

    具体需求 : 仅使用3/5/8升的容器,将开始装满8升的水等分成两份,每份是4升水.要求列出所有等分成功的过程
        输入 : 3个容器的容量和开始水量.对应输入为(3,5,8)和8
        输出 : 初始水量等分两份成功的所有过程. 

    分析 : 首先必须明确的是计算机实现的是模拟人脑中的思考过程.思考过程中用自然语言描述的每一步是由计算机实现的.因此在解决问题前,
           应先描述好自然的解决方案,再由计算机实现或者简化升华.
           从自然的角度来实现 : 
           1.初始,3升容器0升水,5升容器0升水,8升容器8升水.(A,a)(B,b)(C,c),大写表示该容器的容量,小写表示容器此刻的水量.(capacity,size)
           需输入n个容器的初始容量和水量.输入形式暂时简单化.在内部要构成形式 id(capacity,size) ; 
           可以用
           struct container{
                int capacity ; 
                int size ; 
            }
            vector<container>  con; 每次输入则push_back(new container) ; 
            prinf("请输入桶的数量 n ");
            scanf("%d",&n);
            vector<container>  con(n);
            for (int i = 0; i < 输入的桶数量n; ++i)
            {
                prinf("请输入%d号桶的容量",i+1);
                scanf("%d",&con[i].capacity);
                prinf("请输入%d号桶的当前水量",i+1);
                scanf("%d",&con[i].size);
            }


            添加结束后,在创建一个Init表示初始化的状态.con为变化的状态.

           2.由于有3个容器,因此每次有6种倒水动作,分别是3->5,3->8,5->8,5->3,8->3,8->5.很明显由于3/5升的容器水为空.则只有8->3或8->5的倒水动作
           需要两个函数,一是求倒水动作的种类(包括src->des的信息).特点是该函数只需执行一次,存入相应的动作表中.
            struct action{
                int src;
                int des;
            }
            vector<action>  act.这样输出的结果就只需要初始状态和动作列表.
            for (i遍历con.size())
            {
                action 对象.src = i ; 
                for (j遍历con.size()且j!=i)
                {
                    action 对象.des = j ; 
                    act.push_back(对象) ; 
                }
                
            }

            其中动作列表应该用一个栈(或vector)表示(只要尾部可添加删除).用深度遍历.可用stack<int> 或vector<int> actlist.
            遍历每个动作action.判断动作的合理性,合理则压入,计算压入后的状态,判断是否等分成功,如果成功,则输出当前动作列表,然后弹出此动作,
            返回上一层继续遍历.如果失败,则进入到下一层的调用.
            遍历动作结束后,弹出动作.
            开始时需要压入一个0,压入,弹出成对. 
            状态的变化也用一个栈表示,可用stack<vector<container>> 或vector<vector<container>> statuslist.(耗空间)
            ---还可以用一个状态表示,弹出动作的时候进行状态反运算.(耗计算时间).第二种方法不可行,根本计算不了.例如3升有1升水,5升有2升水,---
            ---5->3,则此时3升有3升水.反向处理时3->5,此时状态变成5升水有3升水.跟原状态不符,舍弃---
            
            也就是所,状态的变化和退回都需要用栈的形式存储.是不是太耗费空间了?
            对的,可以在actlist中在添加一个变量值,表示从源桶倒入目的桶的水量.这样就可以实现反向运算.
            struct actinfo{
                int acttype ;
                int water ;
            }
            vector<actinfo> actlist;

            大致如下 : 
            vector<action>  act;
            vector<actinfo> actlist;初始化(压入一个0)
            vector<container>  Init;还有status  初始状态
            int count = 0 ; 

            void  start(&状态)
            {
                for (i遍历act中的每个动作)
                {
                    if(动作合理性和是否状态重复)  插入这里
                    {
                        处理过程(动作,&水量,&状态);
                        actlist.push_back(动作i,水量);
                        if (等分成功)
                        {
                            show actlist,Init(同时记录个数count,以便后面出现0的情况出现提示);
                            反向处理过程(&状态);(actlist.pop_back(动作,水量);处理)
                        }
                        else
                        {
                            start(&状态);
                        }
                    }   
                }
                反向处理过程(&状态);(actlist.pop_back(动作,水量);处理)
            }
            

           二是判断该动作的合理性.只需判断倒入(主动)是否为空,待倒入(被动)是否为满.bool  ???(actcode) ; 
           判断条件具体为return  src.size != 0 && des.size != des.capacity ; 

           3.两种情况,1是8->3,由于8>3,则此时3升容器满,8升容器5升水,即(3,3)(5,0)(8,5).2是8->5,此时为(3,0)(5,5)(8,3)
           --此时处理过程(函数) : 先判断待倒入(被动)的尚需x升水装满,倒入(主动)的容器有y升水,计算y-x的值--
           --如果>=0,则待倒入的容器装满,剩下y-x的正值继续保留在倒入的容器中 ; <0 ,则倒入容器为空,待倒入的容器尚缺x-y的正值--
           需要两个函数
           一是处理过程简化后为 : 先将两桶的水量相加(a+c)与待倒入的容量A.计算a+c-A 的值.
           若 >=0 ,则A容器为满(A,a=A),C容器有a+c-A的值,水量 = A-a; < 0,则A容器为a+c,C容器为空,水量 = c.
           二是判断是否等分成功.遍历所有容器,判断if(size != 总/2 ) { if(size!=0)  return false;}  最外围return true;

    结论 : 需要3个vector类型的变量,分别是
           vector<action>  act;(存放所有动作类型)
           vector<actinfo> actlist;初始化(压入一个0)(存放当前所有动作序列)
           vector<container>  Init;还有status  初始状态(存放初始状态和当前状态)
           需要8个函数.
           InitCon();初始化Init和status 
           InitAct();初始化act
           Start();关键程序,开启执行
           isvalid();动作是否合法
           Handleact();执行动作
           isOK();等分是否成功
           Show();展示结果.
               printf 初始状态.
               for(i遍历当前actlist)
               {
                    Handleact(动作,&水量,&状态);
                    printf 动作,水量,状态.
               }
               printf 遍历结束.
               count++ ; 
           ReHandle(&状态);反向执行操作
               tmp = actlist.pop_back();
               tmp.动作  tmp.水量  状态 ; 
               查询(tmp.动作)找出src和des.将状态中对应于src的+水量,des+水量.返回.

    出现问题 : 当8->3倒入3升水后,3->5倒入3升水,5->3升又倒入3升水.形成(3,0)(5,3)(8,5)和(3,3)(5,0)(8,5)的循环.
               造成循环的真正原因是在动作移动后,新的状态在之前的状态中重复出现.正如状态1->状态2->状态3,而此时
               状态3的下一状态是2,形成一个循环.1->2->3->2->3...
               循环形成的真正条件(原因)?如何打破循环?
               原因是动作(src,des)和水量.在后续的复杂运算等效于之前的动作.即1->a2->b3->c2的动作总效果等价于1->a2的效果.
               动作a,b,c的总效果和动作a相同.即b和c的动作总效果为0.广义上将,需计算在actlist是否存在某一段动作总效果为0的情况.
               具体为src->des为正,des->src为负,总共有3对,分别是3->5,5->3;3->8,8->3;8->5,5->8;
               则有3->5倒入3升水为+3,5->3倒入2升水为-2.相加和为+1.
               方案一 : 需计算在actlist是否存在某一段动作总效果即3对数值为0的情况?
               如果按照现有情况下,则每次添加一个动作,actlist就需从末尾循环向上累加.一旦出现为0的情况,就false,都不为0,返回true;
               消耗大量的计算时间.(判断的结果对数 = 容器数量 = 状态的判断 ,而方案三不需要对属于哪一结果区分对待.)
               方案二 : 利用一个栈存放容器经历的所有状态.每添加一个动作之前,先判断该动作产生的新状态是否在已有队列中.
               消耗空间
               方案三 : 用一个初始状态和actlist,重新计算经历的每一个状态.判断是否与该动作后的状态是否相同,有相同则返回false;都不同,返true;
               选取方案三 . 那么在哪里插入合适呢?
               CONTAIN  tmpInit初始状态,tmpcur当前状态
               处理动作(动作,水量,&tmpcur);得到动作后的状态
               for (i遍历actlist,除了第一个)
               {
                    处理动作(i.动作,水量,&tmpInit);得到下一个状态
                    if(两状态是否相等)
                    {
                        return false ; 
                    }                      
               }
               return true;


                两状态是否相等
                for(j遍历状态)
                {
                    if(tmpInit[j].size !=  tmpcur[j].size)
                    {
                        return false;
                    }
                } 
                return true;


 */
#include <iostream>
#include <vector>

using namespace std;
//函数

class  WaterHalf
{
public:
    struct container{
        int capacity ; 
        int size ; 
    };
    typedef  vector<container>  CONTAIN ;

    struct action{
        int src;
        int des;
    };
    typedef  vector<action>  ACTION ;

    struct actinfo{
        int acttype ; 
        int water ; 
    };
    typedef  vector<actinfo> ACTINFO ;

    void InitAll();
    void Loop();
    bool isvalid(int actcode);
    bool isStatusRep(int actcode); //判断是否会出现状态重复
    bool isequally2(CONTAIN &a,CONTAIN &b); //判断两种状态是否相等
    void Handleact(int actcode,int &water,CONTAIN &tmp );
    bool isOK();
    void ShowAll();
    void ShowContainer(CONTAIN &tmp) ; //显示当前的状态
    void ReHandle();
    void Start(); //启动程序

private:
    CONTAIN     Init,cur ; //Init存储原始状态,cur存储当前状态.
    ACTION      act ;   //动作种类
    ACTINFO     actlist ; //存储当前的动作序列
    int         count ; //所有的可能次数
    int         tatalwater ; //总水量
};

void WaterHalf::InitAll()//vector<container>  Init,cur ;和count = 0
{
    //count
    count = 0 ;
    tatalwater = 0 ;

    //vector<container>  Init,cur和tatalwater
    int n = 0 ;
    int i = 0 ; 
    printf("请输入桶的数量 n : ");
    scanf("%d",&n);
    for (; i < n; ++i)
    {
        container tmp ; 

        printf("请输入%d号桶的容量 : ",i+1);

        scanf("%d",&tmp.capacity);

        printf("请输入%d号桶的当前水量 : ",i+1);

        scanf("%d",&tmp.size);

        tatalwater += tmp.size ; 

        Init.push_back(tmp);
    }

    //cur = Init ;//待处理,需  注意 2
    cur.assign(Init.begin(),Init.end());

    //vector<action>  act;
    i = 0 ;
    for ( ; i < n ; i++)
    {
        action tmp ;

        tmp.src = i;

        for (int j = 0 ; j < n ; j++)
        {
            if ( i != j )
            {
                tmp.des = j ; 

                act.push_back(tmp) ; 
            }
        }
    }

    //vector<actinfo> actlist添加一个特定值(最后pop用)
    actinfo tmp ; 
    tmp.acttype = 0 ; 
    tmp.water = 0 ; 
    actlist.push_back(tmp) ; 
    printf("3升水桶有2升水的输出的格式如(3,2)\n") ; 
}

void WaterHalf::Loop()//不需要&状态,类里本身就提供了cur.
{
    int n = act.size();
    int water = 0 ; 
    for (int actcode = 0 ; actcode < n ; actcode++)
    {
        if(isvalid(actcode)&&isStatusRep(actcode))
        {
            Handleact(actcode,water,cur);
            
            actinfo tmp ; 

            tmp . acttype = actcode ; 

            tmp . water = water ; 

            actlist.push_back(tmp) ; 

            if (isOK())
            {
                ShowAll();
                ReHandle();
            }
            else
            {
                Loop();
            }
        }   
    }
    ReHandle();
}

bool WaterHalf::isvalid(int actcode)
{
    return cur[act[actcode].src].size != 0 &&  cur[act[actcode].des].size != cur[act[actcode].des].capacity ; 
    /*return  src.size != 0 && des.size != des.capacity */; 
}

bool WaterHalf::isStatusRep(int actcode)
{
    //初始化
    CONTAIN tmpInit,tmpcur ;
    int watertmp = 0 ; 
    int curactcode = 0 ;
    tmpInit.assign(Init.begin(),Init.end());
    tmpcur.assign(cur.begin(),cur.end());

    //得到动作后的状态
    Handleact(actcode,watertmp,tmpcur);

    //先跟初始状态判断
    if (isequally2(tmpInit, tmpcur))
    {
        return false ; 
    }

    ACTINFO::iterator  i = actlist.begin() ; 
    i++;//忽略开头的特定值.
    for (; i != actlist.end() ; i++)
    {
        curactcode = i->acttype ; 
        //得到下一个状态
        Handleact(curactcode,watertmp,tmpInit);

        if(isequally2(tmpInit, tmpcur))
        {
            return false ; 
        }
    }
    return true ; 
    
}
bool WaterHalf::isequally2(CONTAIN &a,CONTAIN &b)
{
    int n = Init.size();
    for (int i = 0; i < n; ++i)
    {
        if (a[i].size != b[i].size)
        {
            return false ; 
        }
    }
    return true ;
}
void WaterHalf::Handleact(int actcode,int &water,CONTAIN &tmp )
{
    int watertotal2 = tmp[act[actcode].src].size + tmp[act[actcode].des].size ; 
    int dif = watertotal2 - tmp[act[actcode].des].capacity ; 
    if (dif >= 0 )
    {
        water = tmp[act[actcode].des].capacity - tmp[act[actcode].des].size ; 
        tmp[act[actcode].des].size = tmp[act[actcode].des].capacity ;  
        tmp[act[actcode].src].size = dif ;
    }
    else
    {
        water = tmp[act[actcode].src].size ; 
        tmp[act[actcode].des].size = watertotal2 ;  
        tmp[act[actcode].src].size = 0 ;
    }
}
bool WaterHalf::isOK()
{
    for (CONTAIN::iterator  i = cur.begin(); i != cur.end() ; i++)
    {
        if (i->size != tatalwater/2)
        {
            if ( i->size != 0 )
            {
                return false ;
            }
        }
    }
    return true ;
}
void WaterHalf::ShowAll()
{
    CONTAIN tmp ; 

    tmp.assign(Init.begin(),Init.end());

    int  actcode = 0 ;
    int  water = 0 ;
    count ++ ;

    printf("第%d种情况是\n",count) ; 
    printf("初始状态是\n") ; 

    ShowContainer(tmp) ; 

    ACTINFO::iterator  i = actlist.begin() ; 
    i++;//忽略开头的特定值.
    for (; i != actlist.end() ; i++)
    {
        actcode = i->acttype ; 

        Handleact(actcode,water,tmp);

        printf("%d升桶向%d升桶中倒入%d升水\n",tmp[act[actcode].src].capacity,tmp[act[actcode].des].capacity,water) ; 

        printf("当前状态是") ; 

        ShowContainer(tmp) ; 
    }
    printf("%d种情况结束\n\n",count) ; 

}
void WaterHalf::ShowContainer(CONTAIN &tmp)
{
    for (CONTAIN::iterator i = tmp.begin(); i != tmp.end() ; ++i)
    {
        printf("(%d,%d) ",i->capacity,i->size) ; 
    }
    printf("\n\n") ; 
}

void WaterHalf::ReHandle()
{
    actinfo tmp = actlist[actlist.size()-1];

    actlist.pop_back(); 

    int actcode = tmp.acttype ; 
    int water =  tmp.water ; 
    cur[act[actcode].src].size += water ; 
    cur[act[actcode].des].size -= water ; 
}

void WaterHalf::Start()
{
    InitAll() ; 

    Loop() ; 

    if (count == 0)
    {
        printf("不存在2等分的情况\n") ; 
    }

}

//测试
int main(int argc, char const *argv[])
{
    WaterHalf test;
    test.Start();
    system("pause");
    return 0;
}

转载于:https://www.cnblogs.com/jiangge3/articles/6211535.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值