倒水问题--bfs+Priority_queue+二维标记数组

描述:

   有3个容器(没有刻度),容量分别为a升,b升,c升。其中c升中装满油,另外两个空着。

    要求你只用3个容器操作,最后使得某个容器中正好有d升油。要求求出最小的倒水量。

分析:由于容器没有刻度,那么杯子里面的水肯定是整数。在倒水的时候,我们只能选择把自己倒完或者将别人倒满(没有刻度)。那么就相当于搜索一个隐式的图,图的每一个节点是三个杯子当前的状态,因此我们可以去搜索遍历这个图来达到目的,最优解用bfs。

注意有两个可以优化的地方:

①使用vis数组标记状态时,不用开三维,二维即可。因为总的水量是一定的,那么只要两个状态的前两个杯子里面的水量一样,第三个杯子的水也肯定一样

②可以用优先队列来减少搜索次数,每次选择倒的水量最小的那个节点开始搜索。

好了,分析的差不多了,看代码吧:

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue> 
using namespace std;
struct Node{//状态节点 
	int v[3],dist;//三个被子当前水量和已经倒水的总量 
	bool operator < (const Node& rhs) const{ //重载运算符,优先队列取到水量最小的节点 
	    return dist > rhs.dist;
	}
};
const int maxn = 200 + 5;
int vis[maxn][maxn],cap[3],ans[maxn];
void update_ans(const Node& u){//更新接节点的最小倒水总量 
      for(int i = 0;i < 3;i++){
      	int d = u.v[i];
      	if(ans[d] < 0||u.dist < ans[d]) ans[d] = u.dist;
	  }	
}
void solve(int a,int b,int c,int d){
	cap[0] = a,cap[1] = b,cap[2] = c;
	memset(vis,0,sizeof(vis));
	memset(ans,-1,sizeof(ans));
	priority_queue<Node> q;
	
	Node s;
	s.dist = 0;
	s.v[0] = 0,s.v[1] = 0,s.v[2] = c;
	q.push(s);
	
	vis[0][0] = 1;
	while(!q.empty()){
		Node u = q.top();q.pop();
		update_ans(u);
		if(ans[d]>=0) break;//找到目标状态退出 
		for(int i = 0;i < 3;i++)//模拟杯子i向杯子j倒水 
		  for(int j = 0;j < 3;j++)	  
		    if(i!=j){//不能向自己倒水 
		  	  if(u.v[i]==0||u.v[j]==cap[j]) continue;//i为空或j已满,跳过 
		  	  int amount = min(cap[j],u.v[i]+u.v[j]) - u.v[j];//把j倒满或者把i倒空所需要的倒水量 
		  	  Node u2;
		  	  memcpy(&u2,&u,sizeof(u));
		  	  u2.dist = u.dist + amount; 
		  	  u2.v[i] -= amount;
		  	  u2.v[j] += amount;
		  	  if(!vis[u2.v[0]][u2.v[1]]){//标记状态 
		  	  	 vis[u2.v[0]][u2.v[1]] = 1;
		  	  	 q.push(u2);
			  }
		  }
	}
	while(d >= 0){
		if(ans[d]>=0){
			printf("%d %d",ans[d],d);//可以找到,打印倒水量和目标值 
			return ;
		}
	  d--;
	}
	printf("Impossible!");//找不到 
}
int main() {
     int T,a,b,c,d;
     scanf("%d",&T);
     while(T--){
     	scanf("%d%d%d%d",&a,&b,&c,&d);
     	solve(a,b,c,d);
	 }
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值