HDU_【1495】非常可乐

端午回家嗨之前,刷道题提前嗨~

Problem Description
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

Input
三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。
Output
如果能平分的话请输出最少要倒的次数,否则输出"NO"。
Sample Input
7 4 3
4 1 3
0 0 0
Sample Output
NO
3

思路:bfs
定义状态:{a,b,c,t},分别表示容积为s的瓶子里的可乐体积a、容积为n的瓶子里的可乐体积b,容积为m的瓶子里面的可乐体积c。t则表示从初始状态{s,0,0,0}到达当前状态{a,b,c,t}的最小次数。
很容易看出,当s为奇数时是不可能平分可乐的。
而重复到达的状态是无效的,可以根据这个给dfs剪枝,当状态中的a,b,c任意两个为s/2时,说明存在最小要倒次数。当遍历完整棵状态树时都没有找到平均状态,说明状态树中不存在平均状态。

实现代码:

import java.io.*;
import java.util.*;

public class Main {

	//定义状态
	static class DP{
		int a;  //s中可乐的体积
		int b;  //n中可乐的体积
		int c;  //m中可乐的体积
		
		int t;  //记录达到该状态时   所用最少的次数
		
		public DP(int a,int b,int c,int t) {
			this.a = a;
			this.b = b;
			this.c = c;
			this.t = t;
		}
	}
	

	//非常可乐
	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer cin = new StreamTokenizer(br);
		PrintWriter pr = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
		
		
		while(true) {
			cin.nextToken();
			int s = (int) cin.nval;
			cin.nextToken();
			int n = (int) cin.nval;
			cin.nextToken();
			int m = (int) cin.nval;
			if(m==0&&n==0&&s==0)
				break;
			
			
			Queue<DP> queue = new LinkedList<DP>(); 	
			//定义状态标记   重复到达某状态是无效的
			int[][][] mark = new int[s+1][s+1][s+1];   //可乐的体积是小于101的
			
			
			if((s&1)==1) {
				//奇数是不可能平分的
				pr.println("NO");
				continue;
			}
			
			//将第一个状态放进队列
			DP dp = new DP(s,0,0,0);
			mark[s][0][0] = 1;
			queue.add(dp);
			int res = bfs(queue,mark,s,n,m);
			//如果遍历完所有的状态  都没找到一个平均值的话   就返回false
			if(res == -1)
				pr.println("NO");
			else
				pr.println(res);
		}	
		pr.flush();

	}


	private static int bfs(Queue<DP> queue, int[][][] mark, int s,int n,int m) {
		while(queue.isEmpty()==false) {
			DP dp = queue.poll();
			int[] res;
			DP newdp;
			//s->n
			res = poll(s,dp.a,n,dp.b);
			newdp = new DP(res[0],res[1],dp.c,dp.t+1);
			//如果这个新状态是重复状态    就忽略它
			//只有当新状态不是重复状态时  才将它入队列  并标记已经判断过该状态
			if(mark[newdp.a][newdp.b][newdp.c] != 1) {
				if(newdp.a==s/2&&newdp.b==s/2) 
					return newdp.t;
				if(newdp.a==s/2&&newdp.c==s/2) 
					return newdp.t;
				if(newdp.b==s/2&&newdp.c==s/2)
					return newdp.t;
				mark[newdp.a][newdp.b][newdp.c] = 1;
				//将该状态入队列   以访问其儿子
				queue.add(newdp);
			}
			
			//n->s
			res = poll(n,dp.b,s,dp.a);
			newdp = new DP(res[1],res[0],dp.c,dp.t+1);
			//如果这个新状态是重复状态    就忽略它
			//只有当新状态不是重复状态时  才将它入队列  并标记已经判断过该状态
			if(mark[newdp.a][newdp.b][newdp.c] != 1) {
				if(newdp.a==s/2&&newdp.b==s/2) 
					return newdp.t;
				if(newdp.a==s/2&&newdp.c==s/2) 
					return newdp.t;
				if(newdp.b==s/2&&newdp.c==s/2)
					return newdp.t;
				mark[newdp.a][newdp.b][newdp.c] = 1;
				//将该状态入队列   以访问其儿子
				queue.add(newdp);
			}
			
			
			//s->m
			res = poll(s,dp.a,m,dp.c);
			newdp = new DP(res[0],dp.b,res[1],dp.t+1);
			//如果这个新状态是重复状态    就忽略它
			//只有当新状态不是重复状态时  才将它入队列  并标记已经判断过该状态
			if(mark[newdp.a][newdp.b][newdp.c] != 1) {
				if(newdp.a==s/2&&newdp.b==s/2) 
					return newdp.t;
				if(newdp.a==s/2&&newdp.c==s/2) 
					return newdp.t;
				if(newdp.b==s/2&&newdp.c==s/2)
					return newdp.t;
				mark[newdp.a][newdp.b][newdp.c] = 1;
				//将该状态入队列   以访问其儿子
				queue.add(newdp);
			}
			
			
			//m->s
			res = poll(m,dp.c,s,dp.a);
			newdp = new DP(res[1],dp.b,res[0],dp.t+1);
			//如果这个新状态是重复状态    就忽略它
			//只有当新状态不是重复状态时  才将它入队列  并标记已经判断过该状态
			if(mark[newdp.a][newdp.b][newdp.c] != 1) {
				if(newdp.a==s/2&&newdp.b==s/2) 
					return newdp.t;
				if(newdp.a==s/2&&newdp.c==s/2) 
					return newdp.t;
				if(newdp.b==s/2&&newdp.c==s/2)
					return newdp.t;
				mark[newdp.a][newdp.b][newdp.c] = 1;
				//将该状态入队列   以访问其儿子
				queue.add(newdp);
			}
			
			//n->m
			res = poll(n,dp.b,m,dp.c);
			newdp = new DP(dp.a,res[0],res[1],dp.t+1);
			//如果这个新状态是重复状态    就忽略它
			//只有当新状态不是重复状态时  才将它入队列  并标记已经判断过该状态
			if(mark[newdp.a][newdp.b][newdp.c] != 1) {
				if(newdp.a==s/2&&newdp.b==s/2) 
					return newdp.t;
				if(newdp.a==s/2&&newdp.c==s/2) 
					return newdp.t;
				if(newdp.b==s/2&&newdp.c==s/2)
					return newdp.t;
				mark[newdp.a][newdp.b][newdp.c] = 1;
				//将该状态入队列   以访问其儿子
				queue.add(newdp);
			}
			
			//m->n
			res = poll(m,dp.c,n,dp.b);
			newdp = new DP(dp.a,res[1],res[0],dp.t+1);
			//如果这个新状态是重复状态    就忽略它
			//只有当新状态不是重复状态时  才将它入队列  并标记已经判断过该状态
			if(mark[newdp.a][newdp.b][newdp.c] != 1) {
				if(newdp.a==s/2&&newdp.b==s/2) 
					return newdp.t;
				if(newdp.a==s/2&&newdp.c==s/2) 
					return newdp.t;
				if(newdp.b==s/2&&newdp.c==s/2)
					return newdp.t;
				mark[newdp.a][newdp.b][newdp.c] = 1;
				//将该状态入队列   以访问其儿子
				queue.add(newdp);
			}
		}
		return -1;
	}


	//将容器s中的aL可乐  倒入  容器m中  已知容器m中有bL可乐
	//返回 容器s 中的剩余可乐  和容器m 中的现有可乐
	private static int[] poll(int s, int a, int m, int b) {
		int[] res = new int[2];
		
		int rest = m-b;  //后者能够装可乐的空间
		//如果后者能装下前者全部可乐
		if(rest>=a) {
			//将前者的可乐全部倒入后者
			res[0] = 0;
			res[1] = a+b;
			return res;
		}
		//后者只能装下前者的   rest  容积的可乐
		res[0] = a-rest;
		res[1] = m;  
		
		return res;
	}

}

想上手试试?点我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值