倒水问题 (BFS搜索问题)

题目描述 Description  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold


        有两个无刻度标志的水壶,分别可装 x 升和 y 升 ( x,y 为整数且均不大于 100 )的水。设另有一水 缸,可用来向水壶灌水或接从水壶中倒出的水, 两水壶间,水也可以相互倾倒。已知 x 升壶为空 壶, y 升壶为空壶。问如何通过倒水或灌水操作, 用最少步数能在x或y升的壶中量出 z ( z ≤ 100 )升的水来。

        输入描述 Input Description
         一行,三个数据,分别表示 x,y 和 z;

        输出描述 Output Description
         一行,输出最小步数 ,如果无法达到目标,则输出"impossible"

        样例输入 Sample Input
         3 22 1

        样例输出 Sample Output
        14

解题思路:列举出所有的倒水操作,题目中未给出容器名称,这里假定A容器的容量为x升,B容器的容量为y升

        1、将A容器中的全部水倒入水缸(当A容器中有水时)

        2、将B容器中的全部水倒入水缸(当B容器中有水时)

        3、将A容器装满水(当A容器中的水未满时)

        4、将B容器装满水(当B容器中的水未满时)

        5、A容器中的水倒入B容器(当A容器中有水,同时B容器中水未满时)

        第五种倒水操作还可分为两类:

              情况1:如果A容器中的水小于等于B容器的剩余容量,则操作过后A容器中剩余水量为0;

              情况2:如果A容器中的水大于B容器的剩余容量,则操作过后A容器中剩余水量为:

              A容器当前装水量 - B容器的剩余容量;

        6、B容器中的水倒入A容器(当B容器中有水,同时A容器中水未满时)

         第六种倒水操作还可分为两类:

              情况1:如果B容器中的水小于等于A容器的剩余容量,则操作过后B容器中剩余水量为0;

              情况2:如果B容器中的水大于A容器的剩余容量,则操作过后B容器中剩余水量为:

              B容器当前装水量 - A容器的剩余容量;

实现代码:

​
package algorithm.bfs;

import java.util.LinkedList;
import java.util.Scanner;

/**
 * 题目描述 Description
 *      有两个无刻度标志的水壶,分别可装 x 升和 y 升 ( x,y 为整数且均不大于 100 )的水。
 *      设另有一水 缸,可用来向水壶灌水或接从水壶中倒出的水, 两水壶间,水也可以相互倾倒。
 *      已知 x 升壶为空 壶, y 升壶为空壶。问如何通过倒水或灌水操作, 用最少步数能在x或y升的壶
 *      中量出 z ( z ≤ 100 )升的水 来。
 *
 *      输入描述 Input Description
 *      一行,三个数据,分别表示 x,y 和 z;
 *
 *      输出描述 Output Description
 *      一行,输出最小步数 ,如果无法达到目标,则输出"impossible"
 *
 *      样例输入 Sample Input
 *      3 22 1
 *
 *      样例输出 Sample Output
 *      14
 * @author guojm
 *
 */

class Nodes {
	public int x;   //当前节点的A水壶所装水的体积(升)
	public int y;   //当前节点的B水壶所装水的体积(升)
	public int current; //当前节点所在层数,用于求最小步数
}
public class PourWater {
	int visited[][] = new int[100][100];  //记录该节点是否访问过
	static int a, b, c;
	Nodes n1, n2;
	Nodes t;
	public boolean bfs(int x, int y) {
		boolean ok = false;
        //用LinkedList模拟队列操作
		LinkedList<Nodes> q = new LinkedList<Nodes>();
		n1 = new Nodes();
	    n1.x = x;
	    n1.y = y;
	    n1.current = 0;
	    q.addLast(n1);
	    while(!q.isEmpty()) {
            //取出第一个节点元素(Nodes对象)
	    	n2 = q.getFirst();
            //从模拟队列中移除(Nodes对象)
	    	Nodes x1 = q.removeFirst();
 	
	    	//如果A容器或者B容器得到目标c升水
	    	if(n2.x == c || n2.y == c) {
	    		 //输出当前结点所在的层数
	    		 System.out.println(n2.current);
	    		 //ok设置为true,表明倒水问题有解
	    	     ok = true;           
	    	     break;
	    	}
	    	if(visited[n2.x][n2.y] == 1) continue; //结点重复则剪枝处理
	    	//当前状态没出现过,则标志已访问
	    	visited[n2.x][n2.y] = 1;
	    	
	    	for(int i = 1; i <= 6; i++) {   
	    		 t = new Nodes();
	    		 
	             t.current = n2.current + 1;  //记录每个入栈结点的层数
	             
	             //i=1 并且A容器中有水时,倒水操作为:将A容器中的全部水倒入水 缸
	             if(i == 1 && n2.x > 0)  { t.x = 0; t.y = n2.y;  q.addLast(t); }
	             
	             //i=2 并且B容器中有水时,倒水操作为:将B容器中的全部水倒入水 缸
	             if(i == 2 && n2.y > 0)  { t.y = 0; t.x = n2.x;  q.addLast(t); }
	             
	             //i=3 并且A容器中的水未满时,倒水操作为:将A容器装满水
	             if(i == 3 && n2.x != a) { t.x = a; t.y = n2.y;  q.addLast(t); } 
	             
	             //i=4 并且B容器中的水未满时,倒水操作为:将B容器装满水
	             if(i == 4 && n2.y != b) { t.y = b; t.x = n2.x;  q.addLast(t); }
	             
	             //i=5并且A容器中有水,同时B容器中水未满时,倒水操作为:A容器中的水倒入B容器
	             if(i == 5 && n2.x != 0 && (b - n2.y) != 0) 
	             {  
	            	//如果A容器中的水小于等于B容器的剩余容量
	                if(n2.x <= (b - n2.y))
	                  {  t.x = 0; t.y = n2.y + n2.x;  q.addLast(t); }
	                //如果A容器中的水大于B容器的剩余容量
	                else
	                  {  t.y = b; t.x = n2.x - (b - n2.y); q.addLast(t); }
	             }
	            //i=6并且B容器中有水,同时A容器中水未满时,倒水操作为:B容器中的水倒入A容器
	            if(i == 6 && n2.y != 0 && (a - n2.x) != 0) 
	            {  
	               //如果B容器中的水小于等于B容器的剩余容量
	               if(n2.y <= (a - n2.x))
	                 {  t.y = 0; t.x = n2.x + n2.y; q.addLast(t); }
	               //如果B容器中的水大于A容器的剩余容量
	               else
	                 {  t.x = a; t.y = n2.y - (a - n2.x);  q.addLast(t); }
	            }
	         }
	      }
		return ok;
	}
	public static void main(String[] args) {
		PourWater p = new PourWater();
		@SuppressWarnings("resource")
		Scanner scan = new Scanner(System.in);
		a = scan.nextInt();
		b = scan.nextInt();
		c = scan.nextInt();
		if(!p.bfs(0,0)) System.out.println("impossible");
	}
	

}

​

运行结果:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值