三个水杯
时间限制: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;
}