三个水杯(bfs+字典树)

三个水杯

时间限制:1000 ms  |  内存限制:65535 KB

难度:4

描述 给出三个水杯,大小不一,并且只有最大的水杯的水是装满的,其余两个为空杯子。三个水杯之间相互倒水,并且水杯没有标识,只能根据给出的水杯体积来计算。现在要求你写出一个程序,使其输出使初始状态到达目标状态的最少次数。 输入第一行一个整数N(0<N<50)表示N组测试数据  接下来每组测试数据有两行,第一行给出三个整数V1 V2 V3 (V1>V2>V3 V1<100 V3>0)表示三个水杯的体积。  第二行给出三个整数E1 E2 E3 (体积小于等于相应水杯体积)表示我们需要的最终状态输出每行输出相应测试数据最少的倒水次数。如果达不到目标状态输出-1

样例输入

2

6 3 1

4 1 1

9 3 2

7 1 1

样例输出

3

-1

代码:

/*
第一个问题:
有三个杯子A,B,C,那么就有六种倒法
A-B A-C
B-A B-C
C-A C-B
如A-B,A把水倒进B中,设A为杯中已有的体积,A’为杯中剩余的体积,A”为杯子的总体积
这个题倒水能倒就两种可能:
1:A中的水小于B中的剩余体积,就全倒进去,A=0,B+=A;
2:A中的水大于B中的剩余体积,那么就把B加满,A=A-B‘,B=B“;
第二个问题:
如何保持程序的有穷性
即如何标记是否之前已走过这一步。
在此建立字典树判断是否已存在
比如
6 3 1
4 1 1
案例中出现的情况建立字典树
(6 0 0)(3 3 0)(3 2 1)(5 0 1)(5 1 0)(4 1 1)
                                               root
                                           /   /    \    \
                                        /     |       |     \
                                      /       /       |       \
                                   /          |        |         \
                                /           /          |            \
                            6           3             5              4
                        /              /  \         /  \            /   \
                      0              3     2      0     1          1     2
                     /              /        \    |         \       /      \
                   0              0           1   1            0    1        0
通过Find函数从根节点开始对比查找是否存在过此情况
*/

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;

int
dir[6][2]={1,2,1,3,2,1,2,3,3,1,3,2};//六种倒法
int v1[3],v11[3];

struct
point
{

    int
v[3],step;
}
p,t;
/*************字典树的建立跟查找******************/

struct
node//字典树
{
    node* child[105];
};


void
Build(node *p,struct point &a)//建树
{
    int
i,j,len;
    len=3;
    for
(i=0;i<len;i++)
    {

        if
(p->child[a.v[i]]==NULL)
        {

            node *t=new node;
            for
(j=0;j<105;j++)
                t->child[j]=NULL;
            p->child[a.v[i]]=t;    
        }

        p=p->child[a.v[i]];
    }
}


int
Find(node *p,struct point &a)//查找
{
    int
i,len;
    len=3;
    for
(i=0;i<len;i++)
    {

        if
(p->child[a.v[i]]==NULL)
            return
1;
        p=p->child[a.v[i]];
    }

    return
0;
}

/**************************************************/


int
bfs()//广搜
{
    int
i,x,y,x1;
    node *root=new node;
    for
(i=0;i<105;i++)
        root->child[i]=NULL;   
    queue<point> q;
    t.v[0]=v1[0];
    t.v[1]=0;
    t.v[2]=0;
    t.step=0;
    Build(root,t);
    q.push(t);
    while
(!q.empty())
    {

        t=q.front();
        q.pop();
        if
(t.v[0]==v11[0] && t.v[1]==v11[1] && t.v[2]==v11[2])
            return
t.step;
        for
(i=0;i<6;i++)
        {

            p=t;
            p.step++;
            x=dir[i][0]-1;
            y=dir[i][1]-1;
            if
(t.v[x]>0 && t.v[y]<v1[y])//判断能否倒
            {
                /***********判断是哪种倒法***********/

                x1=v1[y]-t.v[y];//剩余体积
                if(t.v[x]>=x1)
                {

                    p.v[x]=t.v[x]-x1;
                    p.v[y]=v1[y];
                }

                else

                {

                    p.v[x]=0;
                    p.v[y]=t.v[y]+t.v[x];
                }

                /***********************************/

                if
(Find(root,p))//查找此情况是否出现过
                {
                    Build(root,p);//没出现过就此情况建树
                     q.push(p);//入队列
                }
            }
        }
    }

    return
-1;
}


int
main()
{

    //freopen("a.txt","w",stdout);
    int t;
    scanf("%d",&t);
    while
(t--)
    {

        scanf("%d %d %d",&v1[0],&v1[1],&v1[2]);
        scanf("%d %d %d",&v11[0],&v11[1],&v11[2]);
        printf("%d\n",bfs());
    }

    return
0;
}
 

转载于:https://www.cnblogs.com/xiong-/archive/2013/06/11/3131993.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值