【每日一题】土豪打榜

CC 里面有一个土豪很喜欢一位女直播 kiki 唱歌,平时就经常给她点赞、送礼、私聊。最近 CC 直播平台在举行中秋之星直播唱歌比赛,假设一开始该女主播的初始人气值为 start,能够晋升下一轮人气需要刚好达到 end,土豪给主播增加人气可以采取一下三种方法:

  1. 点赞;花费 x C币,人气 + 2
  2. 送礼;花费 y C 币,人气 * 2
  3. 私聊;花费 z C币,人气 - 2
    其中 end 远大于 start 且 end 为偶数,请写一个程序帮助土豪计算一下,最少花费多少 C 币就能帮助该主播 kiki 将人气刚好达到 end,从而能够晋级下一轮?
    限制 0 < x , y , z < = 10000 ; 0 < s t a r t , e n d < 1000000 0<x,y,z<=10000 ;\quad 0<start,end<1000000 0<x,y,z<=10000;0<start,end<1000000
    【例如】
    输入:start = 3,end = 100,x = 1,y = 2,z = 6
    输出:6

示例代码

def min_cost_coins(start, end, x, y, z):
    return process(start, end, x, y, z)

def process(start, end, x, y, z):
  	# basecase 当start 与 end 相等,就满足要求,不需要再花费 C 币了。
    if start == end:
        return 0
    # 尝试点赞
    a = process(start + 2, end, x, y, z) + x
    # 尝试送礼
    b = process(start * 2, end, x, y, z) + y
    # 尝试私聊
    c = process(start - 2, end, x, y, z) + z

    # 决策
    return min(a, b, c)

如下图:只靠 start == end 这个basecase 无法使递推收敛。需要根据题意挖掘其他 basecase,进行剪枝。

剪枝:

  1. start 的值不能为负数
  2. 平凡解:如果我通过只点赞使 start 到达 end,需要 A C币。那么最优解不能大于 A
  3. start 的值可以大于 end 的值,然后通过私聊使 start 退回到 end,但是 start 不能大于 2 * end

在这里插入图片描述

    //start偶数,end偶数  start<=end
    public static int minCcoins1(int add, int times, int del, int start, int end) {
        if (start > end) {
            return -1;
        }
        return process(0, start, end, add, times, del, ((end - start) / 2) * add);
    }

    /** start 人气向 end 改变
     * @param cost      之前已经花了多少钱【可变】
     * @param start     起始人气【可变】
     * @param end       目标人气 【固定】
     * @param add       点赞花费 C 币 【固定】
     * @param times     送礼花费 C 币 【固定】
     * @param del       私聊花费 C 币 【固定】
     * @param limitCoin 已经使用的币大到什么程度不需要再尝试了 【固定】
     * @return
     */
    public static int process(int cost, int start, int end, int add, int times, int del,
                              int limitCoin) {
        if (cost > limitCoin) {
            return Integer.MAX_VALUE;
        }
        if (start < 0) {
            return Integer.MAX_VALUE;
        }
        if (start > (2 * end)) {
            return Integer.MAX_VALUE;
        }
        if (start == end) {
            return cost;
        }

        int min = Integer.MAX_VALUE;
        //让人气-2的方式
        int p1 = process(cost + add, start + 2, end, add, times, del, limitCoin);
        if (p1 != Integer.MAX_VALUE) {
            min = p1;
        }
        //让人气+2的方式
        int p2 = process(cost + del, start - 2, end, add, times, del, limitCoin);
        if (p2 != Integer.MAX_VALUE) {
            min = Math.min(min, p2);
        }
        //让人气*2的方式
        int p3 = process(cost + times, start * 2, end, add, times, del, limitCoin);
        if (p3 != Integer.MAX_VALUE) {
            min = Math.min(min, p3);
        }
        return min;
    }

    public static void main(String[] args) {
        int add = 6;
        int times = 5;
        int del = 1;
        int start = 10;
        int end = 30;
        System.out.println(minCcoins1(add, times, del, start, end));
    }

在这里插入图片描述

    //start偶数,end偶数  start<=end
    public static int minCcoins1(int add, int times, int del, int start, int end) {
        if (start > end) {
            return -1;
        }
        return process(0, start, end, add, times, del, 2 * end, ((end - start) / 2) * add);
    }

    /**
     * end 人气向 start 改变
     *
     * @param cost      之前已经花了多少钱【可变】
     * @param start     起始人气【固定】
     * @param end       目标人气 【可变】
     * @param add       点赞花费 C 币 【固定】
     * @param times     送礼花费 C 币 【固定】
     * @param del       私聊花费 C 币 【固定】
     * @param limitCoin 已经使用的币大到什么程度不需要再尝试了 【固定】
     * @return
     */
    public static int process(int cost, int start, int end, int add, int times, int del,
                              int limitAim, int limitCoin) {
        if (cost > limitCoin) {
            return Integer.MAX_VALUE;
        }
        if (end < 0) {
            return Integer.MAX_VALUE;
        }
        if (end > limitAim) {
            return Integer.MAX_VALUE;
        }
        if (start == end) {
            return cost;
        }

        int min = Integer.MAX_VALUE;
        //让人气-2的方式
        int p1 = process(cost + add, start, end - 2, add, times, del, limitAim, limitCoin);
        if (p1 != Integer.MAX_VALUE) {
            min = p1;
        }

        //让人气+2的方式
        int p2 = process(cost + del, start, end + 2, add, times, del, limitAim, limitCoin);
        if (p2 != Integer.MAX_VALUE) {
            min = Math.min(min, p2);
        }

        if ((end & 1) == 0) {
            //让人气*2的方式
            int p3 = process(cost + times, start, end / 2, add, times, del, limitAim, limitCoin);
            if (p3 != Integer.MAX_VALUE) {
                min = Math.min(min, p3);
            }
        }
        return min;
    }

    public static void main(String[] args) {
        int add = 6;
        int times = 5;
        int del = 1;
        int start = 10;
        int end = 30;
        System.out.println(minCcoins1(add, times, del, start, end));
    }
    public static int minCcoins2(int add, int times, int del, int start, int end) {
        if (start > end) {
            return -1;
        }
        int limitCoin = ((end - start) / 2) * add;
        int limitAim = 2 * end;
        int[][] dp = new int[limitCoin + 1][limitAim + 1];

        for (int cost = 0; cost < limitCoin + 1; cost++) {
            for (int aim = 0; aim < limitAim + 1; aim++) {
                if (aim == end) {
                    dp[cost][aim] = cost;
                } else {
                    dp[cost][aim] = Integer.MAX_VALUE;
                }
            }
        }
        
        for (int cost = limitCoin; cost >= 0; cost--) {
            for (int aim = 0; aim < limitAim + 1; aim++) {
                if (cost + add <= limitCoin && aim + 2 <= limitAim) {
                    dp[cost][aim] = Math.min(dp[cost][aim], dp[cost + add][aim + 2]);
                }

                if (cost + del <= limitCoin && aim - 2 >= 0) {
                    dp[cost][aim] = Math.min(dp[cost][aim], dp[cost + del][aim - 2]);
                }

                if (cost + times <= limitCoin && aim * 2 <= limitAim) {
                    dp[cost][aim] = Math.min(dp[cost][aim], dp[cost + times][aim * 2]);
                }
            }
        }

        return dp[0][start];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值