http://acm.nyist.net/JudgeOnline/problem.php?pid=21
这是一道广搜的题目, 也是我做的第二道广搜题目。
广搜要记住两点:
1.扩展的节点
2.扩展规则
构建解答树, 用队列实现。
1.三个水杯,是大小不一的,初始状态,0号杯子满的,1号2号是空的。
2.0->1, 0->2;
1->0, 1->2;
2->0, 2->1;
共六种情况可能出现。
3.一个不空,另一个不满就可以执行倒水;
4.倒水时,一个杯子可能会把另一个倒满;也可能不能倒满;
5.出现过的状态要进行标记,此处使用了一个三维数组;
6.一旦有一个状态符合,末状态便跳出循环。
注:
1.在列举中间情况时,一种方法是用if将其一一列举出来,但是代码量我竟然写了200多行;另外,还有其他方法,比如使用循环,90行左右。
2.再去寻找下一个杯子时,用了一个“循环数组”, 特别巧妙!
其实,对比两个代码,会发现,在变量和结构体的定义上会有不同,特别注意数组的使用。
(一)代码超长版
#include
#include
#include
#include
#include
using namespace std;
int V1, V2, V3;
int E1, E2, E3;
int N;
typedef struct node
{
int x, y, z;
int step;
}Node;
int flag[110][110][110];
int main(void)
{
scanf("%d", &N);
while(N--)
{
scanf("%d%d%d", &V1, &V2, &V3);
scanf("%d%d%d", &E1, &E2, &E3);
queueQ;
Node head;
memset(flag, 0, sizeof(flag));
head.x = V1;
head.y = 0;
head.z = 0;
head.step = 0;
flag[head.x][head.y][head.z] = 1;
Q.push(head);
while(Q.empty() != 1 || head.x != E1 && head.y != E2 && head.z != E3)
{
Node temp;
head = Q.front();
Q.pop();
if(head.x == E1 && head.y == E2 && head.z == E3)
{
printf("%d\n", head.step);
break;
}
if(head.x != 0 && head.y != V2)//1,2
{
if(head.x + head.y >= V2)
{
temp.x = head.x - (V2 - head.y);
temp.y = V2;
temp.z = head.z;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
else
{
temp.x = 0;
temp.y = head.x + head.y;
temp.z = head.z;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
}
if(head.x != 0 && head.z != V3)//1,3
{
if(head.x + head.z >= V3)
{
temp.x = head.x - (V3 - head.z);
temp.y = head.y;
temp.z = V3;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
else
{
temp.x = 0;
temp.y = head.y;
temp.z = head.x + head.z;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
}
if(head.y != 0 && head.x != V1)//2,1
{
if(head.y + head.x >= V1)
{
temp.x = V1;
temp.y = head.y - (V1 - head.x);
temp.z = head.z;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
else
{
temp.x = head.x + head.y;
temp.y = 0;
temp.z = head.z;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
}
if(head.y != 0 && head.z != V3)//2, 3
{
if(head.y + head.z >= V3)
{
temp.x = head.x;
temp.y = head.y - (V3 - head.z);
temp.z = V3;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
else
{
temp.x = head.x;
temp.y = 0;
temp.z = head.y + head.z;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
}
if(head.z != 0 && head.x != V1)//3, 1
{
if(head.z + head.x >= V1)
{
temp.x = V1;
temp.y = head.y;
temp.z = head.z - (V1 - head.x);
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
else
{
temp.x = head.x + head.z;
temp.y = head.y;
temp.z = 0;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
}
if(head.z != 0 && head.y != V2)//3, 2
{
if(head.z + head.y >= V2)
{
temp.x = head.x;
temp.y = V2;
temp.z = head.z - (V2 - head.y);
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
else
{
temp.x = head.x;
temp.y = head.y + head.z;
temp.z = 0;
if(flag[temp.x][temp.y][temp.z] == 0)
{
temp.step = head.step + 1;
Q.push(temp);
flag[temp.x][temp.y][temp.z] = 1;
}
}
}
if(Q.empty())
{
printf("-1\n");
break;
}
if(temp.x == E1 && temp.y == E2 && temp.z == E3)
{
printf("%d\n", temp.step);
break;
}
}//if
}//while
return 0;
}
(二)稍微简约版
#include
#include
#include
#include
#include
using namespace std;
int V[3];//体积;
int E[3];//末状态;
typedef struct node
{
int state[3];//三个状态;
int step;
}Node;
int N, flag[110][110][110];//标记;
void bfs();
int main(void)
{
scanf("%d", &N);
while(N--)
{
scanf("%d%d%d", &V[0], &V[1], &V[2]);
scanf("%d%d%d", &E[0], &E[1], &E[2]);
memset(flag, 0, sizeof(flag));
bfs();
}//while
return 0;
}
void bfs()
{
queueQ;
Node head;
head.state[0] = V[0];
head.state[1] = 0;
head.state[2] = 0;
head.step = 0;
flag[head.state[0]][head.state[1]][head.state[2]] = 1;
Q.push(head);
while(Q.empty() != 1 || head.state[0] != E[0] && head.state[1] != E[1] && head.state[2] != E[2])
{
Node temp;
head = Q.front();
Q.pop();
if(head.state[0] == E[0] && head.state[1] == E[1] && head.state[2] == E[2])
{
printf("%d\n", head.step);
return;
}
int i, j, k, top;
for(i = 0; i < 3; i++)//3个水杯;
{
for(k = 0, j = i + 1; k < 2; k++, j++)//
{
temp = head;
top = j % 3;
if(temp.state[i] != 0 && temp.state[top] != V[top])// 一个不空,一个不满;
{
if(temp.state[i] + temp.state[top] >= V[top])//一个可以把另一个倒满;
{
temp.state[i] = temp.state[i] - (V[top] - head.state[top]);//head?temp?
temp.state[top] = V[top];
if(flag[temp.state[0]][temp.state[1]][temp.state[2]] == 0)
{
temp.step++;
Q.push(temp);
flag[temp.state[0]][temp.state[1]][temp.state[2]] = 1;
}
}
else //一个不可以把另一个倒满;
{
temp.state[i] = 0;
temp.state[top] = temp.state[top] + head.state[i];
if(flag[temp.state[0]][temp.state[1]][temp.state[2]] == 0)
{
temp.step++;
Q.push(temp);
flag[temp.state[0]][temp.state[1]][temp.state[2]] = 1;
}
}
if(Q.empty())
{
printf("-1\n");
return;
}
if(temp.state[0] == E[0] && temp.state[1] == E[1] && temp.state[2] == E[2])
{
printf("%d\n", temp.step);
return;
}
}//if
}//for
}//while
}//while
}
解题思路:
三个水杯,大小不一,初始状态只有最大的是满的。分别编号0, 1, 2.画一下解答树,也许不难发现是道广搜题,我发现,分析问题时画图还是挺有用的。
1. 一个杯子可以向另外的两个杯子倒水, 所以,对于0, 1, 2号3个杯子,就有6中情况;
2. 倒水的时候,有一个规则:倒水的杯子(主)不空, 被倒水的杯子(宾)不满,就可以倒水;(这是一个判断条件)
3. 在倒水的时候, 又要分两种情况:一种是一个杯子(主)可以把另一个杯子(宾)倒满;一种是不能倒满。相应的要处理的水杯状态就不一样了。
4. 优化代码,就是将原来使用if一一列举的地方使用了for循环,减少了代码量。其中,第一个for循环, i控制遍历0, 1, 2三个水杯,第二个for循环,循环两次,是寻找当前水杯的下一个, 和 下一个的下一个。使用了一个数组,然后又进行了取余操作,类似于“循环队列”,这样就一直可以取0, 1, 2中的数。
5. 优化代码要注意一下,队列为空,找不到目标状态和达到目标状态这两种情况必须在每入队一个时判断一次。
6. 对比两种代码的最大差别就是在类型的定义上:结构的定义,还有体积和末状态的定义都使用了数组,对于那些重复操作就可以使用for循环了。