1. 程式人生 > >caioj1048: 寬搜3(巧妙取量)

caioj1048: 寬搜3(巧妙取量)

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