【實戰篇】| 小鹿教你用動態規劃撩妹的正確方式

都說程序員是直男,聊天聊不過三句,看下邊這位朋友求助小鹿,抱怨說,學習數據結構那麼難,除了優化程序算法,其餘的啥都不能幹,學它幹啥,哎,撩個妹子都撩不到。 前端

image.png
說真的,我都嫌棄他,哈哈,嫌棄歸嫌棄,我還想教給你撩妹的正確方式的,該說不說的,爲了能讓這位朋友能夠撩上妹子,小鹿耗時假期三天時光,出了這篇文章,但願幫助各位宅男們脫單。
故事情節正在生成中......

故事背景git

快過年了,不少吃貨都安奈不住了,也包括愛吃零食的小鹿。天貓超市也不少「滿減活動」。我想不少程序員和小鹿同樣,會在天貓超市有「滿減活動」的時候纔會買一大些零食,一次性吃個夠。程序員

那麼機會來了,你是個吃貨,不少小姐姐也是吃貨,何不借此機撩一撩呢?github

這怎麼撩?憑着你學了數據結構與算法嘛?憑着你是作底層架構的?憑着你會....算法

這點小事,包在小鹿身上了,下面就給你來個用數據結構與算法實戰撩小姐姐,讓小姐姐對你的滿意度從 0 飆升到 100。編程

開始搭訕數組

不少女生說程序員都是直男,沒錯,我也認可我也是其中一位。bash

image

直男是程序員的通病,可是程序員這個職業也賦予了咱們隨機應變能力呀,懂得自救才行(不少宅男們就敗在這了)。數據結構

你不仔細想一想,做爲一個非程序員的妹子,誰喜歡跟你閒着沒事討論這本身不懂的東西。架構

幫小姐姐解決問題

這個時候,該你展示真正的技術了,可不要搞砸了,不認你的印象又被小姐姐大大下降。

那麼問題來了,怎麼選擇購物車中的零食才能知足盡最大可能湊到「滿減活動」呢?

一、動態規劃

咱們就會用到動態規劃去計算,動態規劃在數據結構與算法中屬於重點也是難點,也會涉及到遞歸,這部分也是基礎中最難的。以前不少讀反饋,說分享的教程太基礎,因此關於遞歸基礎,這裏不講了,能夠公衆號後臺回覆「遞歸」,獲取基礎教程。

首先,咱們將複雜問題簡單化,假如咱們購物車中有 2元、4元、2元、3元、6元的五個價格的零食。假如買夠 9 元,咱們立減 5 元,咱們應該湊滿 9 元或者儘量的大於 9 元並接近 9 元呢?

其實這個問題一眼就能選擇出來,可是咱們爲了將複雜問題解決掉,咱們仍是用動態規劃去解決。

其實這個問題相似於遞歸,第一個零食買不買有兩種選擇,一個是買,一個就是不買,第二個、第三個、第四個..... 都有兩種選擇,這時候咱們能夠用到遞歸。

你會說,咱們能夠用回溯算法把全部可能列出來呀,選擇合適的不就行了?記住,咱們是程序員,要儘量的下降時間複雜度,等你所有窮舉出來,估計你的小姐姐早就等不及下單了。

這個問題用動態規劃怎麼解決呢?

咱們用(a,b)來表示每一個零食是否被選擇,a 表示對第 a 個零食是否購買,b 表示已選擇購買零食的總價格。

二、用遞歸樹來表示

上邊的遞歸樹把全部的狀況都列舉了出來,就像是咱們用到的回溯算法,之因此時間複雜度很高,是由於遞歸樹中有的層次不少重複計算的步驟,若是咱們把重複計算的狀態只記錄一種不就能夠減小時間複雜度,提升程序效率了嗎?

上邊的遞歸樹怎麼表示呢?咱們能夠一個二維數組,tree[a][b],a 表示第 a 個零食是否購買,b 表示已選擇購買零食的總價格。

上邊遞歸樹咱們用代碼實現,以下:

/**
 2 * 
 3 * @param prices 各個商品的價格
 4 * @param n 商品的個數
 5 * @param w 滿減條件(滿 199-99元)
 6 */
 7public static void ShoppingSnacks(int[] prices,int n,int w){
 8    boolean[][] tree = new boolean[n][w+1];
 9    tree[0][0] = true;
10    tree[0][prices[0]] = true;
11    //動態規劃
12    for (int i = 1; i < n; i++) {
13        // 不購買當前商品
14        for(int j = 0;j <=w; j++) {
15            //尋找上一個商品決策狀態
16            if(tree[i-1][j] == true) {
17                tree[i][j] = true;
18            }
19        }
20
21        // 購買當前商品
22        for(int j = 0;j <=w-prices[i]; j++) {
23            //尋找上一個商品決策狀態
24            if(tree[i-1][j] == true) {
25                tree[i][j+prices[i]] = true;
26            }
27        }
28     }
29}
複製代碼

上邊代碼執行完成,全部的狀況咱們就用二維數組來表示,零食購買的決策狀態以下:

image

◆ 橫座標表明已經從購物車選擇的零食的總價格;

◆ 縱座標表明對第 i 個零食進行選擇是否購買;

可是上邊的代碼存在一個問題,求得的最大知足滿減條件價格是儘量小於接近 9 元,咱們想要達到的結果是儘量的達到或超過接近滿減條件。怎麼作呢? 咱們作出如下修改,讓滿減價格儘量的大三倍,就是說咱們將知足 9 元提高到 27 元,也就是說,在全部零食中,儘量知足接近 27 元的狀況有哪些,咱們都能選擇出來,接下來咱們選擇出最接近超過 9 元的狀況就能夠了。

代碼修改以下:

1/**
 2 * 
 3 * @param prices 各個商品的價格
 4 * @param n 商品的個數
 5 * @param w 滿減條件(滿 199-99元)
 6 */
 7public static void ShoppingSnacks(int[] prices,int n,int w){
 8    //將商品的價格擴展到三倍
 9    boolean[][] tree = new boolean[n][3*w+1];
10    tree[0][0] = true;
11    tree[0][prices[0]] = true;
12    //動態規劃
13    for (int i = 1; i < n; i++) {
14        // 不購買當前商品
15        for(int j = 0;j <=3*w; j++) {
16            //尋找上一個商品決策狀態
17            if(tree[i-1][j] == true) {
18                tree[i][j] = true;
19            }
20        }
21
22        // 購買當前商品
23        for(int j = 0;j <=3*w-prices[i]; j++) {
24            //尋找上一個商品決策狀態
25            if(tree[i-1][j] == true) {
26                tree[i][j+prices[i]] = true;
27            }
28        }
29     }
30}
31
複製代碼

那麼還有一個問題就是最大可能超過並接近 9 元的狀況咱們選擇出來了,可是怎麼把選擇的哪些零食列出來呢?

咱們利用倒推的方法,這個地方很難理解,能夠本身嘗試着畫圖或者根據代碼和上邊畫的二維數組圖對應着選擇,多看幾遍估計就能夠看會了,問題不大。

怎麼個倒推法呢?假如咱們最後一個商品在數組中用 n-1 表示,若是能夠經過 tree[n-2][ j - prices[i]],或者 tree[i-1][j] 推到出來,那麼這兩種狀態能夠達到,就能夠判斷當前的這個零食是否購買,若是兩種狀態均可以達到,咱們選擇一種就好,覺得有這樣一種狀況就是,到達滿減最接近的值可能有不少中狀況。

上邊介紹完了,那麼給小姐姐選擇零食盡最大可能的接近滿減(199元),咱們就能夠享受立減 99 元的優惠了!

購物車物品以下:

咱們會發現一個問題,小數不能用下標表示,那咱們能夠將價格以及滿減價格同時乘以10,那麼就能夠計算了。

代碼以下:

/**
 2 * 【動態規劃】
 3 * 功能:實現天貓超市 「滿減湊單活動」
 4 * @author 小鹿
 5 *
 6 */
 7public class TBShopping {
 8
 9    /**
10     * 
11     * @param prices 各個零食的價格
12     * @param n 零食的個數
13     * @param w 滿減條件(滿 199-99元)
14     */
15    public static void ShoppingSnacks(int[] prices,int n,int w){
16        //將零食的價格擴展到三倍
17        boolean[][] tree = new boolean[n][3*w+1];
18        tree[0][0] = true;
19        tree[0][prices[0]] = true;
20
21        //動態規劃
22        for (int i = 1; i < n; i++) {
23
24            // 不購買當前零食
25            for(int j = 0;j <=3*w; j++) {
26                //尋找上一個零食決策狀態
27                if(tree[i-1][j] == true) {
28                    tree[i][j] = true;
29                }
30            }
31
32            // 購買當前零食
33            for(int j = 0;j <=3*w-prices[i]; j++) {
34                //尋找上一個商品決策狀態
35                if(tree[i-1][j] == true) {
36                    tree[i][j+prices[i]] = true;
37                }
38            }
39        }
40
41        //找出須要湊單的零食
42        int j;
43        for(j = w;j < 3*w+1; j++) {
44            //在最後一個零食尋找知足最接近 200 的條件狀態
45            if(tree[n-1][j]==true) { 
46                System.out.println("滿減的最大條件爲"+(float)j/10+"元");
47                break;
48            }
49        }
50
51        //沒有可選擇零食
52        if(j == -1) {
53            return;
54        }
55
56        // 倒推遍歷知足條件的零食
57        for(int i = n-1; i>=1; i--) {
58            //當前帳單的總金額大必須於當前零食金額
59            //且上一個商品的決策狀態爲 true
60            if(j - prices[i]>=0 && tree[i-1][j-prices[i]] == true) {
61                //已購買該零食
62                System.out.println(Snacks[i]+p[i]);
63                j = j - prices[i];
64            }else {
65                //沒有購買該零食
66
67            }
68        }
69        if(j != 0) {
70            System.out.print(Snacks[1]+p[0]);
71        }
72    }
73}
複製代碼

運行結果:

當你給小姐姐完成了這項艱鉅的選擇以後,小姐姐的態度就180 度大轉彎了!


本公衆號專一於「前端」、「數據結構」互聯網技術領域,通俗簡單的文字和動漫配圖,讓你愛上編程。

一個不甘平凡的碼農
公衆號:一個不甘平凡的碼農


【Github源碼獲取方式】:https://github.com/luxiangqiang/Data-Structure-Coding/tree/master/動態規劃/滿減活動源碼

【轉載文章請說明出處】:juejin.im/post/5c91f9…

相關文章
相關標籤/搜索