问一个算法的思路 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
waiaan
V2EX    Javascript

问一个算法的思路

  •  
  •   waiaan 2018-08-07 11:15:22 +08:00 6598 次点击
    这是一个创建于 2653 天前的主题,其中的信息可能已经有所发展或是发生改变。

    点外卖的时候不是有满减吗,如何用算法实现最优的点餐组合?即最终合计与满减的值最接近。 比如满 35 减 24,每份菜的价格是已知的,如何算出怎么点菜可以与 35 最接近,当然要比 35 大了。

    34 条回复    2018-08-08 07:38:53 +08:00
    wingkou
        1
    wingkou  
       2018-08-07 11:17:09 +08:00 via Android
    这不就是线性规划吗?
    timboy
        2
    timboy  
       2018-08-07 11:37:43 +08:00
    背包问题?
    murmur
        3
    murmur  
       2018-08-07 11:40:05 +08:00
    需求在哪里呢?
    点餐不是按喜欢吃和吃多少点么
    满 35 减 24 等你点了结账就发现会多出几块钱的饭盒费和派送费
    dingyaguang117
        4
    dingyaguang117  
       2018-08-07 11:42:04 +08:00
    背包问题
    enenaaa
        5
    enenaaa  
       2018-08-07 11:42:15 +08:00
    从全排列开始。逐个条件裁剪掉不必要的路径。再以动态规划策略利用到历史步骤的结果。
    waiaan
        6
    waiaan  
    OP
       2018-08-07 11:45:04 +08:00 via Android
    @murmur 不是,只是偶然想到这个问题,跟具体点餐的事情没关系。
    rabbbit
        7
    rabbbit  
       2018-08-07 11:59:15 +08:00
    允许重复点同一个菜吗?
    geelaw
        8
    geelaw  
       2018-08-07 12:00:55 +08:00
    这个问题是 NP-H,很明显它可以用来解子集和。

    买化妆品的版本(单纯的子集和归约)

    https://geelaw.blog/entries/galeries-lafayette-discount-npc/

    买内裤的版本(有更加复杂的优惠规则)

    https://www.weibo.com/2389742313/GjmvmAQd6
    jasonMakarov
        9
    jasonMakarov  
       2018-08-07 12:01:34 +08:00
    KNN 考虑下吧
    streamo
        10
    streamo  
       2018-08-07 12:02:29 +08:00
    因为你要求具体方案所以不是背包。比较容易想到的办法是用 DFS 求排列组合然后在结果集里挑。
    waiaan
        11
    waiaan  
    OP
       2018-08-07 12:04:34 +08:00 via Android
    @rabbbit 当然可以
    rabbbit
        12
    rabbbit  
       2018-08-07 12:07:57 +08:00
    这个比较像 leetcode 40 题
    给出一个数组和目标值,求数组元素和等于目标值的可能组合
    https://leetcode.com/problems/combination-sum-ii/description/

    想了好久也不出怎么用动态规划做...
    geelaw
        13
    geelaw  
       2018-08-07 12:08:15 +08:00
    @streamo #10 是不是要输出具体方案和是不是背包问题没有必然联系。解决背包问题的动态规划算法可以加上对方案的记录。
    wkan
        14
    wkan  
       2018-08-07 12:10:41 +08:00 via Android
    点最便宜的那个,多点几份是最接近的
    waiaan
        15
    waiaan  
    OP
       2018-08-07 12:17:15 +08:00 via Android
    @rabbbit 差不多是这个意思了
    lychnis
        16
    lychnis  
       2018-08-07 12:20:30 +08:00 via Android
    点外卖 你这样算出来的你愿意吃吗。。。
    streamo
        17
    streamo  
       2018-08-07 12:21:54 +08:00
    @geelaw 想了下觉得你说的对,求具体方案不用 DP 成了我惯性思维了。
    murmur
        18
    murmur  
       2018-08-07 12:23:48 +08:00
    @waiaan 没有算法怎么谈需求
    饿了么这种他为了刺激你消费绝对不会给你最优的背包
    你点一个他就会提示下一档优惠是多少
    不断刺激你加东西
    murmur
        19
    murmur  
       2018-08-07 12:24:01 +08:00
    *更正:没有需求怎么谈算法
    waiaan
        20
    waiaan  
    OP
       2018-08-07 12:25:03 +08:00 via Android
    @murmur
    @lychnis
    只是单纯讨论这个问题的思路
    shakespaces
        21
    shakespaces  
       2018-08-07 12:27:24 +08:00 via Android
    对于外卖这种,一个店里最多也就几十种菜品,直接一个暴力就结束了
    lychnis
        22
    lychnis  
       2018-08-07 12:29:09 +08:00 via Android
    @waiaan 上面有人说了 leetcode n sum 原题。。 但你这个场景就不能硬套
    rabbbit
        23
    rabbbit  
       2018-08-07 12:29:41 +08:00
    搞错了,应该是和 39 题比较像,因为可以重复点菜.

    暴力搜索...
    ```
    var combinatiOnSum= function (candidates, target) {
    let min = Infinity;
    let solution = [];
    len = candidates.length;
    let callback = function (i, target, arr) {
    if (target <= 0 && Math.abs(target) <= min) {
    if (Math.abs(target) === min) {
    solution.push(arr.slice());
    } else {
    solution = [arr.slice()];
    }
    min = Math.abs(target);

    } else if (target > 0) {
    while (i < len) {
    arr.push(candidates[i]);
    callback(i, target - candidates[i], arr);
    arr.pop();
    i++;
    }
    }
    }
    callback(0, target, []);
    return [min, solution];
    };

    console.log(combinationSum([1], 2));
    // [ 0, [ [ 1, 1 ] ] ]
    console.log(combinationSum([1, 2], 3.1));
    // [ 0.8999999999999999, [ [ 1, 1, 1, 1 ], [ 1, 1, 2 ], [ 2, 2 ] ] ]
    console.log(combinationSum([1, 2, 3], 5));
    // [ 0, [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, 2 ], [ 1, 1, 3 ], [ 1, 2, 2 ], [ 2, 3 ] ] ]
    ```
    rabbbit
        24
    rabbbit  
       2018-08-07 12:32:16 +08:00
    waiaan
        25
    waiaan  
    OP
       2018-08-07 12:38:21 +08:00 via Android
    @rabbbit
    ppyybb
        26
    ppyybb  
       2018-08-07 12:44:40 +08:00 via iPhone   1
    如果要求用户至少花费 k 元,那么可以提供一个思路:
    假设不妨设一共 N 个菜,设状态 dp[i][S] 为处理了前 i 个菜,一共点了 S 的价格的状态(0 表示不存在这个状态,1 表示存在这个状态)
    先不考虑满减,显然 dp[i + 1][S + price[i + 1]]
    dp[i+1][S] = dp[S]
    所以 dp[N-1]表示所有菜都考虑了的情况,那么考虑满减 dp[N-1][S] - discount (如果有多个满减,就找最近点那一个)
    结果就是最小的那个

    这种情况如果 S 的总价格不是很大,那么状态不会很多(我们可以假设用户不会点超过 1000 的外卖,S <= 1000,如果超过一般都会超过最大的满减上限了)
    也就是搜索一个 N * S 的状态空间就可以解决

    优化空间: 显然 dp[i]只依赖 dp[i-1],所以两个一维数组就可以解决
    剪枝: 保留当前最小的 S - discount,那么我们可以判断从当前状态出发是否存在更小的可能,这里分类讨论可能的 discount
    缓存: 从业务考虑,我们完全可以先计算好前 100 个状态,然后需要的时候直接从缓存好的状态开始计算起(时间和空间的 tradeoff)
    Biwood
        27
    Biwood  
       2018-08-07 12:51:52 +08:00
    我想说,抛开技术不谈,这类满减活动多半只是为了让你多消费而已。

    判断是否真的优惠只有一个办法,首先别看优惠活动,先选好所有你想要的东西,然后再看优惠活动规则。如果参与优惠比当前订单金额小,那么就是值得的。如果参与优惠后,金额比之前增多,说明你只是用“看起来更便宜”的价格买了你不需要的东西而已。
    MiffyLiye
        28
    MiffyLiye  
       2018-08-07 13:14:42 +08:00
    暴力解法有穷举。
    半暴力解法有 backtracking 和 branch and bound。
    随便加点额外的约束,例如需要包含特定种类、限定特定种类的数量,搜索过程就能快很多。
    ryuzaki113
        29
    ryuzaki113  
       2018-08-07 14:04:42 +08:00
    说个题外话,外卖满减再多不如去店里吃
    zhzer
        30
    zhzer  
       2018-08-07 14:32:12 +08:00
    动态规划
    PulpFunction
        31
    PulpFunction  
       2018-08-07 17:08:56 +08:00
    1。先拿到所有的优惠满减信息
    2。你得有一个预计花钱的参数吧
    3。把 1 里面的价格相减,算出每一个优惠信息的低消
    4。排序 选择一个比预计花钱少的 选一个比预计花钱多的;或者多选点 反正就是几个下标的问题
    PulpFunction
        32
    PulpFunction  
       2018-08-07 17:12:19 +08:00
    5。凑钱 选主食和配菜( 0.获取主食套餐与小吃) 超出预期越少的越实惠,有时候多一点更实惠。。
    stephenyin
        33
    stephenyin  
       2018-08-07 17:54:27 +08:00
    @ryuzaki113 #29 绝大多数外卖满减+平台补贴后都比店里便宜.
    tt67wq
        34
    tt67wq  
       2018-08-08 07:38:53 +08:00 via Android
    有个比较经典的背包问题,跟这个差不多吧
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1240 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 17:47 PVG 01:47 LAX 09:47 JFK 12:47
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86