题目描述
给定两个空杯子容量分别为A, B。 你有六种操作:
1、Fill(i) 把A或者B倒满
2、DROP(i) 把A或者B倒空
3、POUR(i, j) 把 i 倒到 j 里面,不是 i 杯空了就是 j 杯满了
要求:花最少次数使任意一杯被子里面水的容量到C,输出最少次数并记录路径
样例
3 5 4
6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)
思路
对于求最少次数比较容易,用一个状态量 f [ i ] [ j ] 来表示A杯内部水的量和B杯内部水的量,bfs就行,记录路径的话需要当前状态上一次杯中的水量以及操作,在最后以dfs的方法把路径输出。
这边用 choice 函数记录6种操作,print_path 函数用来打印dfs路径
代码中会提供注释
代码片
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
struct Point{
int step, lx, ly, op; //op用来记录操作,lx,ly记录上一次状态容器中水的数量
}f[110][110];
int a,b,c;
struct Volume{
int a, b;
};
void choice(int x ) { //6种操作
if ( x==1 ) printf("FILL(1)\n");
else if ( x==2 ) printf("FILL(2)\n");
else if ( x==3 ) printf("DROP(1)\n");
else if ( x==4 ) printf("DROP(2)\n");
else if ( x==5 ) printf("POUR(1,2)\n");
else if ( x==6 ) printf("POUR(2,1)\n");
}
void bfs(int x, int y) {
queue<Volume> q;
Volume now, next;
now.a=x, now.b=y;
q.push(now);
while(!q.empty()) {
now=q.front(); q.pop();
int itm=f[now.a][now.b].step+1; //当前状态+1,表示操作数
if ( f[a][now.b].step==-1 ) {
f[a][now.b].step=itm;
f[a][now.b].op=1; //记录操作
f[a][now.b].lx=now.a, f[a][now.b].ly=now.b; //记录上一状态,为了可以dfs打印路径
next.a=a, next.b=now.b;
q.push(next);
}
if ( f[now.a][b].step==-1 ) {
f[now.a][b].step=itm;
f[now.a][b].op=2;
f[now.a][b].lx=now.a, f[now.a][b].ly=now.b;
next.a=now.a, next.b=b;
q.push(next);
}
if ( f[0][now.b].step==-1 ) {
f[0][now.b].step=itm;
f[0][now.b].op=3;
f[0][now.b].lx=now.a, f[0][now.b].ly=now.b;
next.a=0, next.b=now.b;
q.push(next);
}
if ( f[now.a][0].step==-1 ) {
f[now.a][0].step=itm;
f[now.a][0].op=4;
f[now.a][0].lx=now.a, f[now.a][0].ly=now.b;
next.a=now.a, next.b=0;
q.push(next);
}
//第五种操作 分为两种情况:被倒入的容器中的水满了或者未满
if ( now.a>b-now.b && f[now.a-(b-now.b)][b].step==-1 ) {
f[now.a-(b-now.b)][b].step=itm;
f[now.a-(b-now.b)][b].op=5;
f[now.a-(b-now.b)][b].lx=now.a, f[now.a-(b-now.b)][b].ly=now.b;
next.a=now.a-(b-now.b), next.b=b;
q.push(next);
}
else if ( now.a<=b-now.b && f[0][now.b+now.a].step==-1 ) {
f[0][now.b+now.a].step=itm;
f[0][now.b+now.a].op=5;
f[0][now.b+now.a].lx=now.a, f[0][now.b+now.a].ly=now.b;
next.a=0, next.b=now.a+now.b;
q.push(next);
}
//第六种操作
if ( now.b>a-now.a && f[a][now.b-(a-now.a)].step==-1 ) {
f[a][now.b-(a-now.a)].step=itm;
f[a][now.b-(a-now.a)].op=6;
f[a][now.b-(a-now.a)].lx=now.a, f[a][now.b-(a-now.a)].ly=now.b;
next.a=a, next.b=now.b-(a-now.a);
q.push(next);
}
else if ( now.b<=a-now.a && f[now.a+now.b][0].step==-1 ) {
f[now.a+now.b][0].step=itm;
f[now.a+now.b][0].op=6;
f[now.a+now.b][0].lx=now.a, f[now.a+now.b][0].ly=now.b;
next.a=now.a+now.b, next.b=0;
q.push(next);
}
}
}
void init() {
for(int i=0; i<=100; i++) {
for(int j=0; j<=100; j++) {
f[i][j].step=-1;
}
}
}
void print_path(int sa, int sb) { //输出路径
if ( sa==0 && sb==0 ) return;
print_path(f[sa][sb].lx,f[sa][sb].ly);
//选择状态
choice(f[sa][sb].op);
}
void solve() {
//int a,b,c;
cin>>a>>b>>c;
init(); //初始化
f[0][0].step=0;
bfs(0,0);
int cnt=inf; //cnt用来记录最少次数
int sa,sb;
//枚举任意一杯中水的容量为c的情况,寻找最少次数并记录其状态
for(int i=0; i<=a; i++ ) {
if ( cnt>f[i][c].step && f[i][c].step!=-1 ) {
cnt=f[i][c].step;
sa=i, sb=c;
}
}
for(int i=0; i<=b; i++) {
if ( cnt>f[c][i].step && f[c][i].step!=-1 ) {
cnt=f[c][i].step;
sa=c, sb=i;
}
}
//
if ( cnt==inf ) printf("impossible\n");
else {
printf("%d\n",cnt);
//以dfs的形式输出路径
print_path(sa,sb);
}
}
int main() {
// freopen("in.txt","r",stdin);
solve();
return 0;
}