问题描述:
给你两个容器,分别能装下A升水和B升水,并且可以进行以下操作
FILL(i) 将第i个容器从水龙头里装满(1 ≤ i ≤ 2);
DROP(i) 将第i个容器抽干
POUR(i,j) 将第i个容器里的水倒入第j个容器(这次操作结束后产生两种结果,一是第j个容器倒满并且第i个容器依旧有剩余,二是第i个容器里的水全部倒入j中,第i个容器为空)
现在要求你写一个程序,来找出能使其中任何一个容器里的水恰好有C升,找出最少操作数并给出操作过程
Input
有且只有一行,包含3个数A,B,C(1<=A,B<=100,C<=max(A,B))
Output
第一行包含一个数表示最小操作数K
随后K行每行给出一次具体操作,如果有多种答案符合最小操作数,输出他们中的任意一种操作过程,如果你不能使两个容器中的任意一个满足恰好C升的话,输出“impossible”
题意:
给你a,b两个容器的容量,六种操作方法,问最少多少次可以使其中一个容器里的水达到体积c,如果不能的话则输出impossible。
思路:
对6种情况进行bfs,难点怎样记录最优情况的倒水过程,用一个三维数组op[i][j][k],表示在a为i升,b为j升时,进行了k次操作。
代码加注释理解:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stdio.h>
using namespace std;
const int Maxn=110;
struct node {
int x,y;
} f1,f2;
int vis[Maxn][Maxn]; //步数和标记
int op[Maxn][Maxn][Maxn]; //记录过程
int a,b,c;
void Operator(int i) //打印过程,与下面的bfs要一一对应
{
switch(i)
{
case 0:
printf("FILL(1)\n");
break;
case 1:
printf("FILL(2)\n");
break;
case 2:
printf("POUR(1,2)\n");
break;
case 3:
printf("POUR(2,1)\n");
break;
case 4:
printf("DROP(1)\n");
break;
case 5:
printf("DROP(2)\n");
break;
}
}
void bfs()
{
int i,j;
queue<node >q;
memset(vis,-1,sizeof(vis));
vis[0][0]=0;
f1.x=0;
f1.y=0;
q.push(f1);
while(!q.empty())
{
f1=q.front();
q.pop();
if(f1.x==c||f1.y==c) //遇到符合条件的就直接打印
{
printf("%d\n",vis[f1.x][f1.y]);
for(i=1; i<=vis[f1.x][f1.y]; i++)
{
Operator(op[f1.x][f1.y][i]);
}
return;
}
for(i=0; i<6; i++) //对6种情况进行遍历
{
if(i==0)
{
f2.x=a;
f2.y=f1.y;
}
if(i==1)
{
f2.x=f1.x;
f2.y=b;
}
if(i==2)
{
f2.y = f1.x + f1.y;
if(f2.y>=b)
{
f2.x=f2.y-b;
f2.y=b;
} else
f2.x=0;
}
if(i==3)
{
f2.x=f1.y+f1.x;
if(f2.x>=a)
{
f2.y=f2.x-a;
f2.x=a;
}
else
f2.y=0;
}
if(i==4)
{
f2.x=0;
f2.y=f1.y;
}
if (i==5)
{
f2.x=f1.x;
f2.y=0;
}
if(vis[f2.x][f2.y]==-1) //对于相同的情况就可以省略,新的就可以记录与进队列
{
vis[f2.x][f2.y]=vis[f1.x][f1.y]+1; //步数加一
for(j=1; j<=vis[f1.x][f1.y]; j++) //对前一种情况的过程,进行拷贝
op[f2.x][f2.y][j]=op[f1.x][f1.y][j];
op[f2.x][f2.y][vis[f1.x][f1.y]+1]=i; //新加入的情况加入原过程
q.push(f2);
}
}
}
puts("impossible");
}
int main()
{
while(~scanf("%d%d%d",&a,&b,&c))
{
bfs();
}
return 0;
}