问题描述:
你有两个罐子,分别有A和B升的体积。可以执行以下操作:
- FILL(i)从水龙头填充锅i (1 ≤ i ≤ 2);
- DROP(i) 将锅i排空至排水管;
- POUR(i,j) 从锅i倒入锅j;在此操作之后,要么锅j已满(锅i 中可能还有一些水),要么锅i是空的(并且其所有内容都已移到锅j 中)。
编写一个程序,找出这些操作的最短可能序列,以便在其中一个锅中产生恰好C升的水。
简而言之,就是有三种方式,一是fill函数将罐中的水灌满,二是将罐子的水全部倒空,三是将i罐子的水全部倒到j罐子里,如果j罐子里的水满了就剩着。然后题目要求只要其中一个罐子的水满足题目要求即可。本题采用广搜算法,一开始两个罐子都是没有水的,然后输入赋值给两个罐子。对于得到最终容量的过程,我们极有可能在回溯的时候搞错下标,所以对于每一步的操作,我们都得对它设置一个pos指针,使它指向前一步操作在queue[]队列的下标,即对下标进行回溯,再顺序输出每一步操作。还有一个问题就是每次记录剩余两个罐子的水量,这里采用中间加个逗号来使其成为字符串而不是两个数字来解决这两个罐子剩余水量的唯一性。
代码:
#include<iostream>
#include<string>
#include<sstream>
#include<map> //用来标记两个罐子剩余水量的头文件
using namespace std;
int v1,v2; //两个瓶子的容量
int c; //目标残余水量
int k1,k2; //记录在某状态时两个瓶子的剩余水量,temporary
typedef class //记录某时刻的状态
{
public:
int x,y; //当前状态(两个瓶子中的水量)
int pos; //记录前一状态在队列queue中的下标
int step; //当前步数
}process;
string combAB(int a,int b) //把整数a、b整合为 "a,b" 的字符串形式(不包括引号),用于标记状态
{
string s;
ostringstream oss;
oss<<a; //输入a b 用“,”隔开成为字符串类型
oss<<',';
oss<<b;
s=oss.str();
oss.str(""); //清空oss对象内所存储的流
return s;
}
void fill(int i) //倒入操作函数
{
switch(i)
{
case 1: {k1=v1; return;}
case 2: {k2=v2; return;}
}
}
void drop(int i) //倒出操作函数
{
switch(i)
{
case 1: {k1=0; return;}
case 2: {k2=0; return;}
}
}
void pour(int i,int j) //i 向j罐子灌满
{
switch(i)
{
case 1: // v1 to v2
{
if(k1+k2<=v2)
{
k2=k1+k2;
k1=0;
}
else
{
k1=k1+k2-v2;
k2=v2;
}
return;
}
case 2: // v2 to v1
{
if(k1+k2<=v1)
{
k1=k1+k2;
k2=0;
}
else
{
k2=k1+k2-v1;
k1=v1;
}
return;
}
}
}
void BFS(void) //对输入水量进行bfs算法操作看是否有解
{
int operation[1000]={0}; //当前步的操作: 1z0:清空z瓶子 2z0:装满z瓶子 3xy:从x瓶倒向y瓶
map<string,bool>vist;
vist["0,0"]=true;
process queue[1000]; //状态队列
int head,tail;
queue[head=0].x=0;
queue[tail=0].y=0;
queue[tail++].step=0;
string ts; //temporary
while(head<tail)
{
process p=queue[head];
if(p.x==c || p.y==c) //得到要求的剩余水量c
{
cout<<p.step<<endl;
//下标回溯,输出操作过程
int ps=p.step;
int* steps=new int[ps+1]; //从1到p.step顺序记录各步操作的下标,不使用steps[0]
steps[ps--]=tail-1;
while(ps)
{
steps[ps]=queue[ steps[ps+1] ].pos;
ps--;
}
for(int i=1;i<=p.step;i++)
{
int temp=operation[ steps[i]-1 ]; //注意各个数组间的下标关系
switch(temp/100)
{
case 1:
{
cout<<"DROP("<<(temp/10)%10<<')'<<endl;
break;
}
case 2:
{
cout<<"FILL("<<(temp/10)%10<<')'<<endl;
break;
}
case 3:
{
cout<<"POUR("<<(temp/10)%10<<','<<temp%10<<')'<<endl;
break;
}
}
}
delete steps;
return;
}
k1=p.x; //装满v1
k2=p.y;
fill(1);
ts=combAB(k1,k2);
if(!vist[ts])
{
vist[ts]=true;
queue[tail].x=k1;
queue[tail].y=k2;
queue[tail].step=p.step+1; //当前的操作步数
queue[tail].pos=head; //前一步操作在queue[]中的下标
operation[tail++]=210; //当前的操作
}
k1=p.x; //装满v2
k2=p.y;
fill(2);
ts=combAB(k1,k2);
if(!vist[ts])
{
vist[ts]=true;
queue[tail].x=k1;
queue[tail].y=k2;
queue[tail].step=p.step+1;
queue[tail].pos=head;
operation[tail++]=220;
}
// 清空v1
k1=p.x;
k2=p.y;
drop(1);
ts=combAB(k1,k2);
if(!vist[ts])
{
vist[ts]=true;
queue[tail].x=k1;
queue[tail].y=k2;
queue[tail].step=p.step+1;
queue[tail].pos=head;
operation[tail++]=110;
}
k1=p.x; //清空v2
k2=p.y;
drop(2);
ts=combAB(k1,k2);
if(!vist[ts])
{
vist[ts]=true;
queue[tail].x=k1;
queue[tail].y=k2;
queue[tail].step=p.step+1;
queue[tail].pos=head;
operation[tail++]=120;
}
k1=p.x; //v1倒向v2
k2=p.y;
pour(1,2);
ts=combAB(k1,k2);
if(!vist[ts])
{
vist[ts]=true;
queue[tail].x=k1;
queue[tail].y=k2;
queue[tail].step=p.step+1;
queue[tail].pos=head;
operation[tail++]=312;
}
k1=p.x; //v2倒向v1
k2=p.y;
pour(2,1);
ts=combAB(k1,k2);
if(!vist[ts])
{
vist[ts]=true;
queue[tail].x=k1;
queue[tail].y=k2;
queue[tail].step=p.step+1;
queue[tail].pos=head;
operation[tail++]=321;
}
head++;
}
cout<<"impossible"<<endl; //如果没有解,就输出impossible
return;
}
int main(void)
{
while(cin>>v1>>v2>>c)
BFS();
return 0;
}
输入:
输出:
俞