#P3734. 第2题-最小总花费
-
ID: 3077
Tried: 55
Accepted: 11
Difficulty: 5
所属公司 :
美团
时间 :2025年9月20日-开发岗
-
算法标签>贪心分类讨论
第2题-最小总花费
解题思路
本题可视为在整数网格上从点 (x,y) 走到 (0,0) 的最短路问题,可用贪心+等价变换直接推公式。
将 ∣x∣,∣y∣ 记为 X,Y,设
- s=min(X,Y):两坐标可“成对”同时变动的最大步数;
- d=∣X−Y∣:成对消去后,单轴上剩余的距离。
关键观察与等价价格:
- 两坐标同号时(含有 0 的情形算同号),一次“同时同向”减少 (1,1) 的最佳价格是 min(b,2a)。 解释:要把 (u,v) 同时向 0 各缩 1,要么用一次联动步 b,要么用两次单点步 2a,取便宜者。
- 两坐标异号时,同时让两坐标的绝对值各减 1(即使用 (1,−1) 型操作)最佳价格是 min(c,2a)。 解释:可用一次平衡步 c,或对两个坐标各做一次单点步 2a,取便宜者。
- 成对消去后只剩单轴距离 d。这时除了直接做 d 次单点(总价 d⋅a),还可以用“联动步+平衡步”的二步组合把单轴每 2 单位降为原点、同时把另一轴往返抵消,二步合价 b+c。 因而“每 2 单位”的最佳价格是 min(2a,b+c),若 d 为奇数,最后再补 1 次单点步 a。
据此分两段计算总价:
- 成对部分: 若 x⋅y≥0,费用 s⋅min(b,2a); 若 x⋅y<0,费用 s⋅min(c,2a)。
- 单轴剩余: 费用 $\left\lfloor\dfrac{d}{2}\right\rfloor\cdot \min(2a,\; b+c) + (d\bmod 2)\cdot a$。
上述策略最优性的理由是:任何能同时减少两坐标绝对值的步骤,其成本下界分别被 min(b,2a) 或 min(c,2a) 所钳制;而当只剩单轴时,所有使另一轴“偏离再回归 0”的序列都可成对配对为(联动+平衡)块,因此每 2 单位的成本下界为 min(2a,b+c),外加必要的 1 次单点步,故整体达到全局最优。
复杂度分析
每组数据只需常数次运算与比较:
- 时间复杂度:O(1)
- 空间复杂度:O(1)
代码实现
Python
import sys
# 计算单组答案的函数
def min_cost(x, y, a, b, c):
X, Y = abs(x), abs(y)
s = min(X, Y) # 可成对同时处理的步数
d = abs(X - Y) # 成对处理后单轴剩余距离
# 判断同号或异号(含 0 视为同号)
same_sign = (x >= 0 and y >= 0) or (x <= 0 and y <= 0)
# 成对部分的最优单价
pair_unit = min(b, 2 * a) if same_sign else min(c, 2 * a)
cost_pairs = s * pair_unit
# 单轴剩余部分:每 2 单位用 min(2a, b+c),若为奇数再补一次 a
two_unit = min(2 * a, b + c)
cost_rem = (d // 2) * two_unit + (d % 2) * a
return cost_pairs + cost_rem
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
t = next(it)
out_lines = []
for _ in range(t):
x = next(it); y = next(it); a = next(it); b = next(it); c = next(it)
out_lines.append(str(min_cost(x, y, a, b, c)))
sys.stdout.write("\n".join(out_lines))
if __name__ == "__main__":
main()
Java
import java.util.*;
public class Main {
// 计算单组答案的函数(使用 long 防止溢出)
static long minCost(long x, long y, long a, long b, long c) {
long X = Math.abs(x), Y = Math.abs(y);
long s = Math.min(X, Y); // 可成对同时处理的步数
long d = Math.abs(X - Y); // 成对处理后剩余的单轴距离
// 含 0 视为同号
boolean sameSign = (x >= 0 && y >= 0) || (x <= 0 && y <= 0);
// 成对部分的最佳单价:同号用 min(b, 2a),异号用 min(c, 2a)
long pairUnit = sameSign ? Math.min(b, 2L * a) : Math.min(c, 2L * a);
long costPairs = s * pairUnit;
// 单轴剩余部分:每 2 单位用 min(2a, b+c),若为奇数再补一次 a
long twoUnit = Math.min(2L * a, b + c);
long costRem = (d / 2) * twoUnit + (d % 2) * a;
return costPairs + costRem;
}
public static void main(String[] args) {
// 使用标准 Scanner 读取输入
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < t; i++) {
long x = sc.nextLong();
long y = sc.nextLong();
long a = sc.nextLong();
long b = sc.nextLong();
long c = sc.nextLong();
sb.append(minCost(x, y, a, b, c)).append('\n');
}
System.out.print(sb.toString());
sc.close();
}
}
C++
#include <bits/stdc++.h>
using namespace std;
// 计算单组答案的函数(使用 long long 防止溢出)
long long min_cost(long long x, long long y, long long a, long long b, long long c) {
long long X = llabs(x), Y = llabs(y);
long long s = min(X, Y); // 可成对同时处理的步数
long long d = llabs(X - Y); // 单轴剩余距离
// 含 0 视为同号
bool sameSign = (x >= 0 && y >= 0) || (x <= 0 && y <= 0);
// 成对部分的最佳单价
long long pairUnit = sameSign ? min(b, 2LL * a) : min(c, 2LL * a);
long long costPairs = s * pairUnit;
// 单轴剩余:每 2 单位用 min(2a, b+c),若为奇数再补一次 a
long long twoUnit = min(2LL * a, b + c);
long long costRem = (d / 2) * twoUnit + (d % 2) * a;
return costPairs + costRem;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
if (!(cin >> t)) return 0;
while (t--) {
long long x, y, a, b, c;
cin >> x >> y >> a >> b >> c;
cout << min_cost(x, y, a, b, c) << "\n";
}
return 0;
}
题目内容
给定两个整数 x 和 y 。你可以进行若干次操作(可以为零次),目标是同时把两者都变为 0 ,每一轮从下列三种操作中任选其一执行:
-
单点步:支付 a 元,将 x 或 y 的一个增加 1 或减少 1 (过程中数值可以为负);
-
联动步:支付 b 元,同时将 x 与 y 各增加 1 或各减少 1(两者同向变化,过程中数值可以为负);
-
平衡步:支付 c 元,将 (x,y) 变为 (x+1,y−1) 或 (x−1,y+1) (两者反向变化,过程中数值可以为负)。
请计算使 (x,y) 变为 (0,0) 的最小总花费。操作次数不限。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 t(1≤t≤104) 代表数据组数,每组测试数据描述如下:
在一行上输入五个整数 x,y,a,b,c(−109≤x,y≤109,1≤a,b,c≤109)
输出描述
对于每一组测试数据,新起一行输出一个整数,表示将 (x,y) 变为 (0,0) 的最小总花费。
样例1
输入
2
3 3 5 3 100
4 -1 2 7 3
输出
9
9
说明
在第二组样例中:先用一次平衡券将 (4,−1)→(3,0) ,花费 c=3 ,剩余 3 步选择样只改动 x ,总花费 3+2+2+2=9 。