【BFS】巧妙取量(c++基础算法)

这道题呢,不是很难,但有那么亿点繁琐,我数了一下,共有七层的嵌套。

本专栏上一篇:【BFS】海上救援任务(c++基础算法)


目录

一.读题

①题面

 ②题意

二.观察与分析

①倒入的操作

②一个特殊情况(特判)

③标记

④因为多组数据

三.AC代码

最后


一.读题

①题面

 【宽搜(难度:6)】巧妙取量

题目描述

【题意】 
    有三个容器,容量分别为 a,b,c(a> b > c ),一开始a装满油,现在问是否只靠abc三个容器量出k升油。
    如果能就输出“yes”,并且说明最少倒几次,否则输出“no”。
    例如:10升油在10升的容器中,另有两个7升和3升的空容器,要求用这三个容器倒油,
    使得最后在abc三个容器中有一个刚好存有5升油,问最少的倒油次数是多少?
(每次倒油,A容器倒到B容器,或者A内的油倒完,或者B容器倒满。 )
  10 7 3 
(10 0 0) 
 (3 7 0):第一次 
 (3 4 3):第二次 
 (6 4 0):第三次 
 (6 1 3):第四次 
 (9 1 0):第五次 
 (9 0 1):第六次 
 (2 7 1):第七次 
 (2 5 3):第八次,出现5了。
【输入格式】
    输入有多组测试数据。
    输入a,b,c, k四个正整数( 100 > = a > b > c > = 1 , 1 < = k < 100 )
【输出格式】
    如果能得到k就输出两行
    第一行"yes",第二行为最少的次数,否则输出"no"。
【样例输入】
10 7 3 5
【样例输出】
yes
8

 ②题意

通过不断地倒入倒出,直到数组中一个量为k,输出最少的操作步数


二.观察与分析

①倒入的操作

针对这题,仅提供三个容器,并且每次只能将一个容器中的油倒入另一个容器,所以由每个状态可以得到的新的状态只有六个。

那么,我们就可以循环i 1-3,j 1-3,模拟i向j倒水。

Ⅰ判断不过要注意有两种情况,这两种情况都是不可以进行倒油操作的:i==j(自己给自己倒油,开什么玩笑);a[i]==0(自己都没油了,还给别人倒油?搞笑)

Ⅱ倒油然后,就可以开始倒油了。定义一个T记录a[j]离倒满所需的油数。这时就会遇到又会两种情况:a[j]没被倒满,a[i]已经空了;a[j]已经满了,而a[i]还有油或刚好倒完。明白这些,程序设计就很简单了……

Ⅲ入队 在此之后,就是熟悉的不能再熟悉的宽搜模板代码了。

现在,看看程序:

	iffor(int i=1;i<=3;i++)
        	{
          		if(q.front().a[i]>0)
            	{
                	for(int j=1;j<=3;j++)
                	{
                    	node sy;
                    	sy=q.front();
                    	sy.ans ++;
                    	if(i!=j)
                    	{
                        	int T=t.a[j]-sy.a[j];
                        	if(sy.a[i]>T)
                        	{
                            	sy.a[j]=t.a[j];
                            	sy.a[i]=sy.a[i]-T;
                        	}
                        	else
                        	{
                            	sy.a[j]+=sy.a[i];
                            	sy.a[i]=0;
                        	}
                        	if(!bo[sy.a[1]][sy.a[2]][sy.a[3]])
                        	{
                            	bo[sy.a[1]][sy.a[2]][sy.a[3]]=1;
                            	if(sy.a[1]==k||sy.a[2]==k||sy.a[3]==k)
                            	{
                                	flag=true;
                            	}
                            	q.push(sy);
                        	}
                    	}
                	}

②一个特殊情况(特判)

当目标状态与初始状态一样时,要直接输出一个yes,0(因为当出现上述情况时,无需操作即可达到目标状态)

③标记

因为有可能达不到目标状态,因此需要用bool数组(初始值为false)当到达目标状态时,标记为true。详情请看后面的代码。

④因为多组数据

如题,因为有多组数据,我们要做一些烦人的操作。

1.输入

要用到EOF这样的东西。

EOF:当你将一个文件输入到一个函数中时,它总是会返回一个状态,它是读出来的,或者是失败的,它是如何表达的?所以就约定俗成定义一个标识符表示这个状态,就有了EOF(end of file)。如果第一个参数是 NULL (null),那么该 scanf函数就可以返回 EOF,否则将会传回已被成功地设置和分配的参数数目(>=0)。所以,这个循环,将是一个死循环。(while:实现循环表达的一种,循环的内容只能是一个语句,可以是一个简单的语句,还可以是复合语句。)

2.初始化 

用memset()函数初始化数组,用下面的语句初始化队列。
 

while(!q.empty())
    	{
        	q.pop();
    	}

很简单,原理就不用我讲了。。。 


三.AC代码

#include<bits/stdc++.h>
using namespace std;
struct node{
    int ans,a[10];
};
queue<node>q;
int k;
bool bo[105][105][105],flag;
int main()
{
    node t,p;
    while(scanf("%d%d%d%d",&t.a[1],&t.a[2],&t.a[3],&k)!=EOF)//多组数据
    {
        flag=false;
        memset(bo,0,sizeof(bo));
         
    	bo[t.a[1]][t.a[2]][t.a[3]]=1;
    	p.ans=0;
    	p.a[1]=t.a[1];
    	p.a[2]=p.a[3]=0;
    	q.push(p);
     	if(q.front().a[1]==k||q.front().a[2]==k||q.front().a[3]==k)
    	{
    	    printf("yes\n%d\n",0);
            continue;
    	}//特判 
    	while(!q.empty())
    	{
        	for(int i=1;i<=3;i++)
        	{
          		if(q.front().a[i]>0)
            	{
                	for(int j=1;j<=3;j++)
                	{
                    	node sy;
                    	sy=q.front();
                    	sy.ans ++;
                    	if(i!=j)
                    	{
                        	int T=t.a[j]-sy.a[j];
                        	if(sy.a[i]>T)
                        	{
                            	sy.a[j]=t.a[j];
                            	sy.a[i]=sy.a[i]-T;
                        	}
                        	else
                        	{
                            	sy.a[j]+=sy.a[i];
                            	sy.a[i]=0;
                        	}
                        	if(!bo[sy.a[1]][sy.a[2]][sy.a[3]])
                        	{
                            	bo[sy.a[1]][sy.a[2]][sy.a[3]]=1;
                            	if(sy.a[1]==k||sy.a[2]==k||sy.a[3]==k)
                            	{
                                	flag=true;
                            	}
                            	q.push(sy);
                        	}
                    	}
                	}
            	}
          
        	}//遍历每一种倒法 
        if(flag) break;
        q.pop();
    	}
    	if(flag)
    	{
        	printf("yes\n%d\n",q.back().ans);
    	}
    	else
    	{
        	printf("no\n");
    	}
    	while(!q.empty())
    	{
        	q.pop();
    	}
    }
 
}

最后

最近在训练,就水水文章吧(好像一直在水……)。。。

给个小红心吧!!!😁

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lyh不会打代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值