CC 里面有一个土豪很喜欢一位女直播 kiki 唱歌,平时就经常给她点赞、送礼、私聊。最近 CC 直播平台在举行中秋之星直播唱歌比赛,假设一开始该女主播的初始人气值为 start,能够晋升下一轮人气需要刚好达到 end,土豪给主播增加人气可以采取一下三种方法:
- 点赞;花费 x C币,人气 + 2
- 送礼;花费 y C 币,人气 * 2
- 私聊;花费 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,进行剪枝。
剪枝:
- start 的值不能为负数
- 平凡解:如果我通过只点赞使 start 到达 end,需要 A C币。那么最优解不能大于 A
- 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];
}