这也是一个类似倒水问题的题
把两个杯子中的水量形成的有序数组(x,y),看作有向图中的一个顶点,起点是(0,0),终点是一个杯子的水量为C。
若一种状态A可以通过对某个杯子的某种操作转移到另一个状态B,那么图中有一条从A指向B的边。
求起点到终点的最短路,BFS即可。注意几点
1. 因为这道题需要打印路径,所以需要多设置一个保存操作的结构体数组,递归打印操作即可:我的做法是从目标终点出发,沿记录递归到起点,把输出语句放在递归语句的后面,就可以实现顺序打印。
2.对于目标水量等于某个杯子的容量的情况,可以直接输出1
3. 我使用了数组模拟队列,因次状态结构体数组和操作结构体数组的下标是对应的,如果你使用了STL中的queue,那么你需要在状态结构体中增加一个分量以记录产生这个状态的操作在操作数组中的下标,以便记录每一个节点的parent
对于三种操作作用在两个杯子上的六种情况,我选择了直接全部写出来,有别的办法就别这么写,好麻烦
AC代码如下
#include<cstdio>
#define MAX 10201//两个杯子至多都有101种状态,总状态数不会超过101*101=10201
struct operation
{
char name;//记录操作的种类
int k,oprand;//k表示该店的parent的下标,oprand表示操作的是哪个杯子(0或1)
};
struct state
{
int p[2], th;//数组p表示两个杯子中的水量,th表示经过了几组操作
};
int cap[2], tar,ans;
bool vis[101][101];
state q[MAX];
operation op[MAX];
inline int min(int a,int b)
{
return a < b ? a : b;
}
int bfs()
{
for (int i = 0; i <= cap[0]; i++)
for (int j = 0; j <= cap[1]; j++)
vis[i][j] = 0;
int front = 0, rear = 1;
q[0].th = q[0].p[0] = q[0].p[1] = 0;
while (front < rear)
{
state& t = q[front];
//fill和drop产生的状态不可能是终点,故不用检验
if (t.p[0] < cap[0]&&!vis[cap[0]][t.p[1]])
{
vis[cap[0]][t.p[1]] = 1;
q[rear].p[0] = cap[0];
q[rear].p[1] = t.p[1];
q[rear].th = t.th + 1;
op[rear].k = front;
op[rear].name = 'F';
op[rear].oprand = 0;
rear++;
}
if (t.p[1] < cap[1] && !vis[t.p[0]][cap[1]])
{
vis[t.p[0]][cap[1]] = 1;
q[rear].p[0] = t.p[0];
q[rear].p[1] = cap[1];
q[rear].th = t.th + 1;
op[rear].k = front;
op[rear].name = 'F';
op[rear].oprand = 1;
rear++;
}
if (t.p[0] >0 && !vis[0][t.p[1]])
{
vis[0][t.p[1]] = 1;
q[rear].p[0] = 0;
q[rear].p[1] = t.p[1];
q[rear].th = t.th + 1;
op[rear].k = front;
op[rear].name = 'D';
op[rear].oprand = 0;
rear++;
}
if (t.p[1]>0 && !vis[t.p[0]][0])
{
vis[cap[0]][t.p[1]] = 1;
q[rear].p[0] = t.p[0];
q[rear].p[1] = 0;
q[rear].th = t.th + 1;
op[rear].k = front;
op[rear].name = 'D';
op[rear].oprand = 1;
rear++;
}
if (t.p[0]>0&&t.p[1]<cap[1])
{
int dao = min(t.p[0], cap[1] - t.p[1]);
if (!vis[t.p[0] - dao][t.p[1] + dao])
{
vis[t.p[0] - dao][t.p[1] + dao] = 1;
q[rear].p[0] = t.p[0] - dao;
q[rear].p[1] = t.p[1] + dao;
q[rear].th = t.th + 1;
op[rear].k = front;
op[rear].name = 'P';
op[rear].oprand = 0;
if (q[rear].p[0] == tar || q[rear].p[1] == tar)
{
ans = q[rear].th;
return rear;
}
rear++;
}
}
if (t.p[1]>0 && t.p[0]<cap[0])
{
int dao = min(t.p[1], cap[0] - t.p[0]);
if (!vis[t.p[0] + dao][t.p[1] - dao])
{
vis[t.p[0] + dao][t.p[1] - dao] = 1;
q[rear].p[0] = t.p[0] + dao;
q[rear].p[1] = t.p[1] - dao;
q[rear].th = t.th + 1;
op[rear].k = front;
op[rear].name = 'P';
op[rear].oprand = 1;
if (q[rear].p[0] == tar || q[rear].p[1] == tar)
{
ans = q[rear].th;//记录步数
return rear;
}
rear++;
}
}
front++;
}
return -1;//没有找到终点,表示搜索失败
}
void display(int k)//先递归后输出,以实现从终点出发,但是以顺序打印步骤
{
if (k) display(op[k].k);//不是起点,向前递归
else//是起点,输出步数
{
printf("%d", ans);
return;
}
if (op[k].name == 'P') printf("\nPOUR(%d,%d)", op[k].oprand + 1, !op[k].oprand + 1);
else if (op[k].name == 'F') printf("\nFILL(%d)", op[k].oprand+1);
else printf("\nDROP(%d)", op[k].oprand+1);
}
int main()
{
scanf("%d%d%d", &cap[0], &cap[1], &tar);
for(int i=0;i<2;i++)
if (tar == cap[i])
{
printf("1\nFILL(%d)", i+1);
return 0;
}
int t = bfs();
if (t == -1) printf("impossible");
else display(t);
return 0;
}