题目链接

题目描述

种商品,每种有三元组 :重量、初始体积、压缩系数。将它们自上而下堆成一条竖直货堆。设商品 上方(不含自身)的总重量为 ,则其实际体积为 。希望通过调整堆放顺序,使所有商品实际体积之和最小。

解题思路

  • 目标函数为
    因此等价于最大化
  • 若堆放顺序为 (自上而下),则 ,从而
    对任意两件商品 ,若令 上方,则对这对商品贡献为 ;反之为 。为了使总贡献最大,应当让较大的一个作为“在上方”的次序成立:
  • 结论:按比值 从大到小排序(实现时用交叉乘比较 以避免精度),自上而下堆放,即可使总实际体积最小。
  • 扫描计算答案:维护前缀重量 (表示“当前物品上方的总重量”),每次累加 ,再令

代码

#include <bits/stdc++.h>
using namespace std;
using int64 = long long;

struct Item { long long w, v, k; };

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; if (!(cin >> n)) return 0;
    vector<Item> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i].w >> a[i].v >> a[i].k;

    sort(a.begin(), a.end(), [](const Item& A, const Item& B){
        __int128 lhs = (__int128)A.w * B.k;
        __int128 rhs = (__int128)B.w * A.k;
        if (lhs != rhs) return lhs > rhs; // 按 w/k 降序
        return A.w < B.w; // 次关键字不影响最优性
    });

    long long ans = 0;
    long long prefixW = 0; // 当前物品上方的总重量
    for (const auto& x : a) {
        ans += x.v - x.k * prefixW;
        prefixW += x.w;
    }
    cout << ans << '\n';
    return 0;
}
import java.util.*;
import java.math.*;

public class Main {
    static class Item { long w, v, k; Item(long w, long v, long k){ this.w=w; this.v=v; this.k=k; } }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Item[] a = new Item[n];
        for (int i = 0; i < n; i++) a[i] = new Item(sc.nextLong(), sc.nextLong(), sc.nextLong());
        Arrays.sort(a, (A, B) -> {
            BigInteger lhs = BigInteger.valueOf(A.w).multiply(BigInteger.valueOf(B.k));
            BigInteger rhs = BigInteger.valueOf(B.w).multiply(BigInteger.valueOf(A.k));
            int cmp = lhs.compareTo(rhs);
            if (cmp != 0) return -cmp; // 降序
            return Long.compare(A.w, B.w);
        });
        long ans = 0, prefixW = 0;
        for (Item x : a) { ans += x.v - x.k * prefixW; prefixW += x.w; }
        System.out.println(ans);
    }
}
from functools import cmp_to_key

n = int(input())
items = [tuple(map(int, input().split())) for _ in range(n)]  # (w, v, k)

def cmp(a, b):
    w1, v1, k1 = a
    w2, v2, k2 = b
    x = w1 * k2
    y = w2 * k1
    if x != y:
        return -1 if x > y else 1  # 按 w/k 降序
    return -1 if w1 < w2 else (1 if w1 > w2 else 0)

items.sort(key=cmp_to_key(cmp))

ans = 0
prefixW = 0
for w, v, k in items:
    ans += v - k * prefixW
    prefixW += w

print(ans)

算法及复杂度

  • 算法:按 降序排序(用交叉乘比较),线性扫描维护前缀重量并累加
  • 时间复杂度
  • 空间复杂度