poj-3414 Pots(BFS 回溯)

此篇博客介绍了一个编程问题,目标是通过一系列FILL、DROP和POUR操作,将水从一个或两个指定容积的壶中转移,以达到特定容积C。博主提供了两种解决方案,一种是使用二维数组记录回溯路径,另一种是模拟队列的简化版代码。
摘要由CSDN通过智能技术生成

Pots

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 33057 Accepted: 13667 Special Judge
Description

You are given two pots, having the volume of A and B liters respectively. The following operations can be performed:

FILL(i) fill the pot i (1 ≤ i ≤ 2) from the tap;
DROP(i) empty the pot i to the drain;
POUR(i,j) pour from pot i to pot j; after this operation either the pot j is full (and there may be some water left in the pot i), or the pot i is empty (and all its contents have been moved to the pot j).
Write a program to find the shortest possible sequence of these operations that will yield exactly C liters of water in one of the pots.

Input

On the first and only line are the numbers A, B, and C. These are all integers in the range from 1 to 100 and C≤max(A,B).

Output

The first line of the output must contain the length of the sequence of operations K. The following K lines must each describe one operation. If there are several sequences of minimal length, output any one of them. If the desired result can’t be achieved, the first and only line of the file must contain the word ‘impossible’.

Sample Input

3 5 4

Sample Output

6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)

题意:

给定两个壶,一个容积为A、另一个容积为B,使它们通过FILL、DROP、POUR操作(对1或2进行,共6种操作),使得其中一个壶的容量变为C。
思路:
搜索的过程不说了,说说输出,我们知道广搜的特点就是每走一步就可能通过当前步延伸出多步,若我们通过父节点纪录子节点的方式就行不通了(这里的父节点指的是先走的步,子节点是先走的步扩展出来的步),所以我们要使子节点纪录父节点,因为一个子节点只有一个父节点,那如何记录呢,我采用的是数组的方式,每一个节点对应一个数组下标rec[j],数组是一个二维数组,rec[j][0]代表父节点的下标,rec[j][1]对应当前步的操作,还要注意我设定的当作开始的节点的节点的父节点是0,那么数组下标0就不能对应操作了,那么我就让控制数组下标的变量从1开始,这样也就找到了输出时的出口,输出时就按这样输出就可以了

代码:

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int A,B,C;
int j=1;//j表示各节点下标
bool vis[120][120];
int rec[10000][2]={0};//rec是二维数组,每个节点对应一个rec[j],
//rec[j][0]表示该节点的父节点下标,rec[j][1]代表当前节点操作 
struct pots{
	int a,b;
	int step;
	int self;
};
queue<pots> Q;
//FILL1 和 FILL2 
void FILL(pots &temp,pots p,int s)
{
	if(s==1){
	temp.a=A;
    temp.b=p.b;
	}
	else if(s==2){
		temp.a=p.a;
        temp.b=B;
	}
	temp.step=p.step+1;
    temp.self=j;
}
//DROP1 和 DROP2 
void DROP(pots &temp,pots p,int s)
{
	if(s==1){
		temp.a=0;
		temp.b=p.b;
	}
	else if(s==2){
		temp.a=p.a;
		temp.b=0;
	}
	temp.step=p.step+1;
	temp.self=j;
}
//POUR 1->2 和 POUR 2->1 
void POUR(pots &temp,pots p,int s1,int s2)
{
	if(s1==1&&s2==2){
		if(p.b+p.a<=B){
			temp.b=p.b+p.a;
			temp.a=0;
		}
		else{
			temp.a=p.a-(B-p.b);
			temp.b=B;
		}
	}
	else if(s1==2&&s2==1){
		if(p.a+p.b<=A){
			temp.a=p.a+p.b;
			temp.b=0;
		}
		else{
			temp.b=p.b-(A-p.a);
			temp.a=A;
		}
	}
	temp.step=p.step+1;
	temp.self=j;
}
//打印结果,通过回溯不断寻找父节点,最开始的父节点下标为0 
void print(int a)
{
	if(a==0){
		return;
	}
	print(rec[a][0]);
	switch (rec[a][1]) {
        case 1:printf("FILL(1)\n");break;
        case 2:printf("FILL(2)\n");break;
        case 3:printf("DROP(1)\n");break;
        case 4:printf("DROP(2)\n");break;
        case 5:printf("POUR(1,2)\n");break;
        case 6:printf("POUR(2,1)\n");break;
    }
    return;
}
//判断是否达到结束条件(其中一个pot的volumn==C)
//如果没有则给队列添加新结点,如果有修改ok退出 
void Do(pots pp,pots p,int &ok,int s)
{
	if(!vis[pp.a][pp.b])
	{
		rec[j][0]=p.self;
		rec[j][1]=s;
		j++;
		vis[pp.a][pp.b]=true;
		Q.push(pp);
	}
	if(pp.a==C||pp.b==C) {
		ok=p.step+1;
	}
}
int main()
{
	int ok=0;
	for(int k=0;k<120;k++){
		for(int l=0;l<120;l++){
			vis[k][l]=false;
		}
	}
	//freopen("data.txt","r",stdin);
	cin>>A>>B>>C;
	
	pots p,pp;
	p.a=0;p.b=0;p.step=0;p.self=0;
	Q.push(p);
	while(!Q.empty()){
		p=Q.front();
		for(int i=1;i<=6;i++){
			switch(i){
				case 1:{
					FILL(pp,p,1);
					Do(pp,p,ok,1);
					break;
				}
				case 2:{
					FILL(pp,p,2);
					Do(pp,p,ok,2);
					break;
				}
				case 3:{
					DROP(pp,p,1);
					Do(pp,p,ok,3);
					break;
				}
				case 4:{
					DROP(pp,p,2);
					Do(pp,p,ok,4);
					break;
				}
				case 5:{
					POUR(pp,p,1,2);
					Do(pp,p,ok,5);
					break;
				}
				case 6:{
					POUR(pp,p,2,1);
					Do(pp,p,ok,6);
					break;
				}
			}
			if(ok) break;
		}
		if(ok) break;
		//cout<<pp.a<<" "<<pp.b<<" "<<pp.self<<" "<<pp.step<<endl;
		Q.pop();
	}
	if(!ok)
	printf("impossible\n");
	else
   {
	 printf("%d\n",ok);
	 print(j-1);
   }
	return 0;
}

参考博客链接:https://blog.csdn.net/xiaoqiang179/article/details/81232431

更多:

对比上面的做法,我在网上还看到了一种模拟队列的做法,感觉代码更加简洁。代码和博客链接如下:
博客:https://blog.csdn.net/sdut_jk17_zhangming/article/details/80501475

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <cstdio>
using namespace std;
int a,b,c,top;
int vis[110][110];
struct node
{
    int x,y;//状态
    int w; //到达该状态的步骤
    int last;//上一个状态
    int step;//到达该状态需要几步
}ans[1000000];
void pri(int x) //六种方法打印
{
    if(x==0)
        printf("FILL(1)\n");
    if(x==1)
        printf("FILL(2)\n");
    if(x==2)
        printf("POUR(2,1)\n");
    if(x==3)
        printf("POUR(1,2)\n");
    if(x==4)
        printf("DROP(1)\n");
    if(x==5)
        printf("DROP(2)\n");
}
void yes(int x) //回溯
{
    if(ans[x].last==0)
    {
        pri(ans[x].w);
        return ;
    }
    yes(ans[x].last);
    pri(ans[x].w);
}
void bfs()
{
    int head,tail,i;
    int aa,bb;
    head = 0,tail = 1;
    while(head<=tail) //模拟队列
    {
        node x = ans[head++];
        if(x.x==c||x.y==c) //成功
        {
            cout<<ans[head-1].step<<endl;
            yes(head-1);
            return ;
        }
        for(i=0;i<6;i++) //六种操作
        {
            if(i==0)
            {
                aa = a;
                bb = x.y;
            }
            if(i==1)
            {
                aa = x.x;
                bb = b;
            }
            if(i==2)
            {
                aa = min(a,x.x+x.y);
                bb = max(0,x.y-(a-x.x));
            }
            if(i==3)
            {
                aa = max(0,x.x-(b-x.y));
                bb = min(b,x.x+x.y);
            }
            if(i==4)
            {
                aa = 0;
                bb = x.y;
            }
            if(i==5)
            {
                aa = x.x;
                bb = 0;
            }
            if(!vis[aa][bb])
            {
                ans[tail].x = aa;
                ans[tail].y = bb;
                ans[tail].w = i;
                ans[tail].step = x.step+1;
                ans[tail++].last = head-1;
                vis[aa][bb] = 1;
            }
        }
    }
    cout<<"impossible"<<endl;
}
int main()
{
    int i;
    while(cin>>a>>b>>c)
    {
        memset(vis,0,sizeof(vis));
        top = 0;
        ans[top].x = 0;
        ans[top].y = 0;
        ans[top].step = 0;
        ans[top++].last = 0; //初始状态为空
        vis[0][0] = 1;
        bfs();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值