LeetCode 365.水壺問題

題目

水壺問題
鏈接可能點(diǎn)不進(jìn)去锉矢,所以我把完整的題目寫在了下面藕溅。

有兩個容量分別為x升和y升 的水壺以及無限多的水棚壁。請判斷能否通過使用這兩個水壺杯矩,從而可以得到恰好z升的水?
如果可以袖外,最后請用以上水壺中的一或兩個來盛放取得的z升水史隆。
你允許:
*裝滿任意一個水壺
*清空任意一個水壺
*從一個水壺向另外一個水壺倒水,直到裝滿或者倒空

示例 1:
輸入: x = 3, y = 5, z = 4
輸出: True

示例 2:
輸入: x = 2, y = 6, z = 5
輸出: False

分析

題意非常清晰曼验,沒有異議逆害。對于示例 1我們可以通過如下步驟得到4:

裝滿y:0 5
把y倒入x:3 2
把x清空:0 2
裝滿y:2 5
把y倒入x:3 4
最后我們從y這個杯子中得到了4。

這題的標(biāo)簽是數(shù)學(xué)蚣驼,一般數(shù)學(xué)標(biāo)簽的題都是有規(guī)律可循或者它本質(zhì)就是一個數(shù)學(xué)定理魄幕。比如這道題燈泡開關(guān)答案是sqrt(n),這道題我是通過找規(guī)律做出來的颖杏,手撕了n=1-8所有的情況后發(fā)現(xiàn)的纯陨。
回到這道題,開始看到這道題我一點(diǎn)思路都沒有,做了半年的算法了發(fā)現(xiàn)數(shù)學(xué)和動態(tài)規(guī)劃標(biāo)簽的題是最難找到思路的翼抠。于是乎我去谷歌了一下咙轩,在我寫這篇文章的時候我搜到的思路千篇一律:判斷z是否能被x和y的最大公約數(shù)整除;反正我是想不到的阴颖。
看來這確實(shí)是一個定理沒跑了活喊。那有沒有什么其他的方法來做這道題呢?

Accept

其實(shí)這道題你可以類比為有向連通圖量愧,路徑由題目中給出的三種操作創(chuàng)建钾菊,節(jié)點(diǎn)就是兩杯水的多少,然后利用廣度/深度搜索算法來做這個題偎肃。

解釋一下路徑怎么來
對于示例 1煞烫,最開始兩杯水都是0 0。
0 0和0 5之間存在一條路徑累颂,因?yàn)樗麄兛梢酝ㄟ^第一個操作進(jìn)行連接滞详。
為什么是有向圖
依然對于示例 1,假設(shè)目前兩杯水為2 2紊馏。
2 2能到達(dá)3 1料饥,只需要y的水倒入x即可,但是反過來3 1是沒辦法直接到達(dá)2 2的朱监。

廣度/深度搜索需要一個起始節(jié)點(diǎn)岸啡,我們可假設(shè)一個初始狀態(tài)比如裝滿x來模擬。
因?yàn)榇擞邢驁D中存在環(huán)赌朋。

解釋一下為何存在環(huán)
依然對于示例 1
0 0能到0 5凰狞,0 5同樣能到0 0。

所以我們需要記憶之前走過的點(diǎn)沛慢,后續(xù)不要再走赡若,否則會死循環(huán)。
此題我們用廣度來做即可团甲,深度也一樣的思路就不再給出了逾冬。

bool canMeasureWater(int x, int y, int z) {
        //去掉不可能的情況
        if(z > x + y) {
            return false;
        }
        //去掉一目了然的情況
        if(z == 0 || z == x || z == y || z == x + y) {
            return true;
        }
        //利用廣度搜索來判斷,最開始我們裝滿x,并把x和y轉(zhuǎn)成一維來表示,這個值同時也類比于廣度搜索中的節(jié)點(diǎn)躺苦。
        //num = x * 10000 + y numberMap用來記錄已經(jīng)走過的路身腻,后續(xù)不會再走,你也可以用其他方式表示節(jié)點(diǎn)匹厘,后續(xù)解析的時候?qū)?yīng)著來就好了嘀趟。
        //這里使用了哈希表,因?yàn)槲覀兒竺娼?jīng)常會查詢此表愈诚,把查詢時間降低為O(1)提升算法速度她按。
        unordered_map<int, bool> numberMap;
        numberMap[x * 10000 + 0] = true;
        //雙端隊(duì)列牛隅,存放需要遍歷的點(diǎn)。
        deque<int> needFindDeque;
        needFindDeque.push_back(x * 10000 + 0);
        //如果可以繼續(xù)走酌泰,那就一直走
        while (!needFindDeque.empty()) {
            //得到此時兩個瓶子的值
            int frontValue = needFindDeque.front();
            needFindDeque.pop_front();
            //這里應(yīng)該不難理解吧媒佣,一維還原為二維
            int frontX = frontValue / 10000;
            int frontY = frontValue % 10000;
            //看看是否能得到z
            if(z == frontX || z == frontY || z == frontX + frontY) {
                return true;
            }
            //不能得到z,那么我們現(xiàn)在有題目中的幾條路可以走
            //1.裝滿x
            {
                int tempX = x;
                //如果已經(jīng)走過了陵刹,不需要添加
                if(numberMap[tempX * 10000 + frontY] == false) {
                    numberMap[tempX * 10000 + frontY] = true;
                    needFindDeque.push_back(tempX * 10000 + frontY);
                }
            }
            //2.裝滿y
            {
                int tempY = y;
                //如果已經(jīng)走過了默伍,不需要添加
                if(numberMap[frontX * 10000 + tempY] == false) {
                    numberMap[frontX * 10000 + tempY] = true;
                    needFindDeque.push_back(frontX * 10000 + tempY);
                }
            }
            //3.清空x
            {
                //如果已經(jīng)走過了,不需要添加
                if(numberMap[0 * 10000 + frontY] == false) {
                    numberMap[0 * 10000 + frontY] = true;
                    needFindDeque.push_back(0 * 10000 + frontY);
                }
            }
            //4.清空y
            {
                //如果已經(jīng)走過了衰琐,不需要添加
                if(numberMap[frontX * 10000 + 0] == false) {
                    numberMap[frontX * 10000 + 0] = true;
                    needFindDeque.push_back(frontX * 10000 + 0);
                }
            }
            //5.x倒入y
            {
                //如果x全部倒入y也糊,y都沒能滿,重點(diǎn)5舛O陨琛框弛!
                if(frontY + frontX < y) {
                    int tempX = 0;
                    int tempY = frontY + frontX;
                    if(numberMap[tempX * 10000 + tempY] == false) {
                        numberMap[tempX * 10000 + tempY] = true;
                        needFindDeque.push_back(tempX * 10000 + tempY);
                    }
                }
                //x倒入y辛辨,還有剩余
                else {
                    int tempX = frontX - (y - frontY);
                    int tempY = y;
                    if(numberMap[tempX * 10000 + tempY] == false) {
                        numberMap[tempX * 10000 + tempY] = true;
                        needFindDeque.push_back(tempX * 10000 + tempY);
                    }
                }
            }
            //6.y倒入x
            {
                //如果y全部倒入x,x都沒能滿瑟枫,重點(diǎn)6犯恪!慷妙!
                if(frontX + frontY < x) {
                    int tempX = frontY + frontX;
                    int tempY = 0;
                    if(numberMap[tempX * 10000 + tempY] == false) {
                        numberMap[tempX * 10000 + tempY] = true;
                        needFindDeque.push_back(tempX * 10000 + tempY);
                    }
                }
                //y倒入x僻焚,還有剩余
                else {
                    int tempX = x;
                    int tempY = frontY - (x - frontX);
                    if(numberMap[tempX * 10000 + tempY] == false) {
                        numberMap[tempX * 10000 + tempY] = true;
                        needFindDeque.push_back(tempX * 10000 + tempY);
                    }
                }
            }
        }
        //如果所有可能的路徑都做完了還不能得到z,那直接返回false
        return false;
}
通過代碼
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膝擂,一起剝皮案震驚了整個濱河市虑啤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌架馋,老刑警劉巖狞山,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叉寂,居然都是意外死亡萍启,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門屏鳍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勘纯,“玉大人,你說我怎么就攤上這事钓瞭〔底瘢” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵山涡,是天一觀的道長堤结。 經(jīng)常有香客問我搏讶,道長,這世上最難降的妖魔是什么霍殴? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任媒惕,我火速辦了婚禮,結(jié)果婚禮上来庭,老公的妹妹穿的比我還像新娘妒蔚。我一直安慰自己,他們只是感情好月弛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布肴盏。 她就那樣靜靜地躺著,像睡著了一般帽衙。 火紅的嫁衣襯著肌膚如雪菜皂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天厉萝,我揣著相機(jī)與錄音恍飘,去河邊找鬼。 笑死谴垫,一個胖子當(dāng)著我的面吹牛章母,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翩剪,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼乳怎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了前弯?” 一聲冷哼從身側(cè)響起蚪缀,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恕出,沒想到半個月后询枚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剃根,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年哩盲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狈醉。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡廉油,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苗傅,到底是詐尸還是另有隱情抒线,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布渣慕,位于F島的核電站嘶炭,受9級特大地震影響抱慌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜眨猎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一抑进、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧睡陪,春花似錦寺渗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汁果,卻和暖如春涡拘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背据德。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工鳄乏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晋控。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓汞窗,卻偏偏與公主長得像姓赤,于是被迫代替她去往敵國和親赡译。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354