caioj1048: 宽搜3(巧妙取量)

版权声明:有女朋友的老江的博客,转载请告知老江 https://blog.csdn.net/qq_42367531/article/details/83377982

1048: 宽搜3(巧妙取量)题目在此

时间限制: 1 Sec  内存限制: 128 MB

题目描述

【题目描述】 
有三个容器,容量分别为 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

思路:首先的话,我们看到了这道题之后,第一感觉一定是搜索对吧,而且因为是要一直判断的,所以我们可以使用宽搜,这个都是可以理解的吧。然后这道题目,其实在一定意义上面就是我们小学做过的一种奥数题的类型,真是扎心,接下来,按照老规矩,要审题,这道题目需要我们输出两个,同时是多组数据,这个是要注意的,然后看样例得到的过程,不难看出,这个就是一个换罐子,把多的移到少的,一直凑啊凑啊的过程那么这个思路就出来了,宽搜+多次判断

这个判断有六种情况我在这里列举一下吧

用6个判断,来判断x,y,z两两之间的最小值,进行换罐子 
6种情况如下:
1.my-tno.y,tno.x   2.mz-tno.z,tno.x   3.mx-tno.x,tno.y
4.mz-tno.z,tno.y   5.mx-tno.x,tno.z   6.my-tno.y,tno.z

到这里都能够理解的默默举个爪子呗,很好(我是不是有点自作多情了,没关系继续),这道题的数据说真的还真不小,反正我定了1110000,然后就编目录,通俗讲就是定义结构体,而且有一个要注意的就是,因为要判断是否可以输出答案,就要用到一个bool bk来判断,如果等于true,说明有答案可以输出,如果等于false,说明没有答案输出,总体来讲大概就是这样子吧,让我再瞄一眼代码啊,哦哦哦哦哦有个

大大大重点:那就是我们在把原来的数据保存在结构体里面的时候,要把三个数结合起来保存,这样就可以只用一个数组来保存,否则就要用三个,这样就会大大大大大的浪费空间和时间,

什么意思呢?就是比如说:  a=55 , b=76 , c=33,那么如果我们用常人的思维,是不是用三个数组来保存再带人结构体里面,这样不说都知道会浪费很多的空间,最骇人的是这样要判断多次,很容易出错。所以我们就要把 a*10000+b*100+c=55*10000+76*100+33=557633,那么这样就是在不影响他本身的情况下,用一个数组来保存,就可以使得代码更加简洁,同时判断起来也更加方便,bool数组的判断也只用一个就okk了。

大致的思路就是这样,剩下的看具体代码的实现吧,代码也写得非常清楚了,不懂的就就就再看几遍吧

/*
这种做法就是一直判断,就是把每一种情况都列出来,找出最早成立的那一个 
还有一个就是,因为是一种一种情况来判断的
所以用队列来处理也是可以的 
然后我们要用6个判断,来判断x,y,z两两之间的最小值,进行换罐子 
6种情况如下:
1.my-tno.y,tno.x   2.mz-tno.z,tno.x   3.mx-tno.x,tno.y
4.mz-tno.z,tno.y   5.mx-tno.x,tno.z   6.my-tno.y,tno.z           
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int x,y,z,s;//x,y,z表示的就是题目中的a,b,c;s表示的是步数 
}list[1110000];
bool v[1110000];//判断重复的 
int kk,head,tail,mx,my,mz;//head和tail不解释 
inline int ys(node tno) {return tno.x*10000+tno.y*100+tno.z;}
/*
这是节省空间的一步,就比如说:22 55 77
那么经过了ys这一步之后就变成了  225577
把三个数拼在一起变成一个数来判断
如果不用这一步的话,就要分三个来保存自然就会浪费空间 
*/ 
inline int min(int x,int y) {return x<y?x:y;}
inline int max(int x,int y) {return x>y?x:y;}
int main()
{
    while(scanf("%d%d%d%d",&mx,&my,&mz,&kk)!=EOF)//多组数据 
    {
        list[1].x=mx; list[1].s=list[1].y=list[1].z=0;
		/*
		这一步就是初始化,就是在最开始把所有的油都放在最大的罐子里
		那么其他的东西都为0,因为油都放在最大的罐子mx里
		my和mz就是空的,步数也是空的 
		*/ 
        head=1; tail=2; memset(v,false,sizeof(v));
		/*
		这个理解成初始化,也就是说,把这个过程变成了一个委屈巴巴的队列,
		head=1,tail=2是为了方便,这样就能清楚看出当前的这个队列是有人的 
		*/ 
        v[ys(list[1])]=true;//第一种情况 
		bool bk=false;//bk是用来判断输出的 
        while(head<tail)//这样就表示当前这个队列有人 
        {
            node tno=list[head]; int k;
			//定义tno是为了不打那么多,防止出错 
			//k是用来找最小的来换罐子,按照做法可以这么理解 
            if(tno.x==kk || tno.y==kk || tno.z==kk)//如果有一个满足题目的要求 
			{
				printf("yes\n%d\n",tno.s);//就输出yes并输出步数(tno.s代表经过的步数) 
				bk=true; break;//退出 
			}
            k=min(my-tno.y,tno.x);
			/*
			如果没有满足题目的条件,就进行换罐子,
			用最小值来给油换罐子(第二个罐子的总存量-第二个罐子当前的量和第一个罐子的量做比较)
			(这一步是寻找要换的罐子) 
			*/ 
            tno.x-=k; tno.y+=k;//这个表示的就是换罐子的过程 
            if(!v[ys(tno)])
			/*
			这一步就是判断重复的过程,ys上面解释过了,
			然后在上面的第一次换罐子的时候我们将第一种情况记录下来了,
			也就是说,只要当前的情况和成立过的情况不一样就是OK的
			*/ 
            {
                v[ys(tno)]=true; tno.s++;
				/*
				进入了循环之后,说明我们找到了一种新的情况,
				这个时候就要用v数组把这种情况保存起来,用来后面判断,
				然后这个时候,我们是进行了下一轮的换罐子,
				所以tno.s++,就是在步数那里增加一种情况
				*/
                if(tno.x==kk || tno.y==kk || tno.z==kk)
				{
					printf("yes\n%d\n",tno.s);
					bk=true; break;
				}//同样的判断,如果成立就输出,不成立就退出,进行下一步的操作
                list[tail]=tno; tail++;
				/*
				这一步就是把当前这一种情况的值保存在list这个结构体里面,
				解释一下:list[head]表示的是第一种情况,然后list[tail]就是用来保存每一种情况的状态,
				然后我们在进行下一步之前,要把tno的状态变回list[head]的状态,
				这样才可以保证不会有情况漏掉,也可以保证每一种情况的6种状态都可以经历一次
				*/
            }
            tno=list[head];//恢复初始状态
            k=min(mz-tno.z,tno.x);//寻找要换的罐子
            tno.x-=k; tno.z+=k;//换罐子
            if(!v[ys(tno)])
            {
                v[ys(tno)]=true; tno.s++;
                if(tno.x==kk || tno.y==kk || tno.z==kk)
				{
					printf("yes\n%d\n",tno.s);
					bk=true; break;
				}
                list[tail]=tno; tail++;
            }
            tno=list[head];
            k=min(mx-tno.x,tno.y);
            tno.y-=k; tno.x+=k;//和之前的一样
            if(!v[ys(tno)])
            {
                v[ys(tno)]=true; tno.s++;
                if(tno.x==kk || tno.y==kk || tno.z==kk)
				{
					printf("yes\n%d\n",tno.s);
					bk=true; break;
				}
                list[tail]=tno; tail++;
            }
            tno=list[head];
            k=min(mz-tno.z,tno.y);
            tno.y-=k; tno.z+=k;//和之前的一样
            if(!v[ys(tno)])
            {
                v[ys(tno)]=true; tno.s++;
                if(tno.x==kk || tno.y==kk || tno.z==kk)
				{
					printf("yes\n%d\n",tno.s);
					bk=true; break;
				}
                list[tail]=tno;tail++;
            }
            tno=list[head];
            k=min(mx-tno.x,tno.z);
            tno.z-=k; tno.x+=k;//和之前的一样
            if(!v[ys(tno)])
            {
                v[ys(tno)]=true; tno.s++;
                if(tno.x==kk || tno.y==kk || tno.z==kk)
				{
					printf("yes\n%d\n",tno.s);
					bk=true; break;
				}
                list[tail]=tno; tail++;
            }
            tno=list[head];
            k=min(my-tno.y,tno.z);
            tno.z-=k; tno.y+=k;//和之前的一样
            if(!v[ys(tno)])
            {
                v[ys(tno)]=true; tno.s++;
                if(tno.x==kk || tno.y==kk || tno.z==kk)
				{
					printf("yes\n%d\n",tno.s);
					bk=true; break;
				}
                list[tail]=tno; tail++;//和之前的一样
            }
            head++;
			/*
			但是这个时候我们就已经把head=1的时候的状态的6种情况都跑了一遍,
			这个时候就是跑第二种情况要面临的6种状态,接着第三种一直到可以与题目相同为止
			*/
        }
        if(bk==false) printf("no\n");
		/*
		如果bk在跑完了所以之后,仍然等于false,
		说明没有可以成立的状态,那么这么时候就输出no
		*/
    }
    return 0;
}

简直就是大大大大的okk,要是有大佬来临的话我想放一个蒟蒻不懂的代码,望大佬点评。

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
struct node
{
    int a,b,c,step;
}s,e;
int a,b,c,k;
bool f[105][105][105];
int bfs(node x)
{
    queue<node> q;
    q.push(x);
    f[x.a][x.b][x.c]=true;
    while(!q.empty())
    {
        node cur=q.front();
        q.pop();
        if(cur.a==k||cur.b==k||cur.c==k)
        {
            return cur.step;
        }
        if(cur.a>0&&cur.b<b)
        {     
              int t=min(cur.a,b-cur.b);
              node temp;
              temp.a=cur.a-t;
              temp.b=cur.b+t;
              temp.c=cur.c;
              temp.step=cur.step+1;
              if(!f[temp.a][temp.b][temp.c])
              {
                q.push(temp);
                f[temp.a][temp.b][temp.c]=1;
              }
        }
        if(cur.a>0&&cur.c<c){   
          int t=min(cur.a,c-cur.c);
          node temp;
          temp.a=cur.a-t;
          temp.b=cur.b;
          temp.c=cur.c+t;
          temp.step=cur.step+1;
          if(!f[temp.a][temp.b][temp.c])
          {
            q.push(temp);
            f[temp.a][temp.b][temp.c]=1;
          }
        }
        if(cur.b>0&&cur.c<c)
        {     
              int t=min(cur.b,c-cur.c);
              node temp;
              temp.a=cur.a;
              temp.b=cur.b-t;
              temp.c=cur.c+t;
              temp.step=cur.step+1;
              if(!f[temp.a][temp.b][temp.c])
              {
                    q.push(temp);
                    f[temp.a][temp.b][temp.c]=1;
              }
        }
        if(cur.b>0&&cur.a<a)
        {    
          int t=min(cur.b,a-cur.a);
          node temp;
          temp.a=cur.a+t;
          temp.b=cur.b-t;
          temp.c=cur.c;
          temp.step=cur.step+1;
          if(!f[temp.a][temp.b][temp.c])
          {
                q.push(temp);
                f[temp.a][temp.b][temp.c]=1;
          }
        }
        if(cur.c>0&&cur.a<a)
        {    
          int t=min(cur.c,a-cur.a);
          node temp;
          temp.a=cur.a+t;
          temp.b=cur.b;
          temp.c=cur.c-t;
          temp.step=cur.step+1;
          if(!f[temp.a][temp.b][temp.c])
          {
            q.push(temp);
            f[temp.a][temp.b][temp.c]=1;
          }
        }
        if(cur.c>0&&cur.b<b)
        {   
          int t=min(cur.c,b-cur.b);
          node temp;
          temp.a=cur.a;
          temp.b=cur.b+t;
          temp.c=cur.c-t;
          temp.step=cur.step+1;
          if(!f[temp.a][temp.b][temp.c])
          {
            q.push(temp);
            f[temp.a][temp.b][temp.c]=1;
          }
        }
    }
    return -1; 
} 
int main()
{
    while(scanf("%d%d%d%d",&a,&b,&c,&k)!=EOF)
    {
        s.a=a,s.b=0,s.c=0,s.step=0;
        memset(f,0,sizeof(f));
        int ans=bfs(s);
        if(ans==-1) printf("no\n");
        else
        {
            printf("yes\n%d\n",ans);
        }
    }
    return 0;
}

我大致也看了一下,发现其实和我原来的代码本质是一样的,也是判断那六种情况:

用6个判断,来判断x,y,z两两之间的最小值,进行换罐子 
6种情况如下:
1.my-tno.y,tno.x   2.mz-tno.z,tno.x   3.mx-tno.x,tno.y
4.mz-tno.z,tno.y   5.mx-tno.x,tno.z   6.my-tno.y,tno.z 

 

猜你喜欢

转载自blog.csdn.net/qq_42367531/article/details/83377982
今日推荐