算法思想
先假设下标越大牛越接近地面。
只考虑怎么摆放第i个牛和第(i+1)个牛,前面第0~(i-1)个牛摆没摆好可以不管,因为他们的重量总和同时出现在不等式的两边,化简后可以去掉。
问题转变为了比较牛A和牛B在“A上B下”和“B上A下”这两种情况下的最大风险。
比较算法
// a[0]--牛A的重量,a[1]--牛A的力量;b[0]--牛B的重量,b[1]--牛B的力量
bool cmp(std::array<int32_t, 2> const &a, std::array<int32_t, 2> const &b)
{
const int32_t risk_ab = std::max(-a[1], a[0] - b[1]);
const int32_t risk_ba = std::max(-b[1], b[0] - a[1]);
return risk_ab < risk_ba;
}
对return的表达式进行化简
// 等价的表达式
// risk_ab < risk_ba
// risk_ab + a[1] + b[1] < risk_ba + a[1] + b[1]
// std::max(b[1], a[0] + a[1]) < std::max(a[1], b[0] + b[1])
// 已知牛的重量大于0,所以有
// a[0] + a[1] > a[1],b[0] + b[1] > b[1]。
// 下面分情况讨论被std::max操作隐藏掉的大小比较:
// 0. 如果 b[1] > a[0] + a[1],则必有 b[0] + b[1] > b[1] > a[0] + a[1] > a[0],直接得出比较结果;
// 1. 如果 b[1] < a[0] + a[1],则比较 a[0] + a[1] 和 std::max(a[1], b[0] + b[1]);
// 1-0. 如果 a[1] > b[0] + b[1],则必有 a[0] + a[1] > a[1] > b[0] + b[1] > b[0],直接得出比较结果;
// 1-1. 如果 a[1] < b[0] + b[1],则比较 b[0] + b[1] 和 a[0] + a[1]。
// 可以发现std::max(b[1], a[0] + a[1])和std::max(a[1], b[0] + b[1])的比较结果,
// 与 a[0] + a[1] 和 b[0] + b[1] 的比较结果是等价的。
return的表达式比较最终化简为了 a[0]+a[1]<b[0]+b[1],即比较 w[i]+s[i] 和 w[i+1]+s[i+1],但是并没有这个必要。
完整代码
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <vector>
#include <array>
bool cmp(std::array<int32_t, 2> const &a, std::array<int32_t, 2> const &b)
{
const int32_t risk_ab = std::max(-a[1], a[0] - b[1]);
const int32_t risk_ba = std::max(-b[1], b[0] - a[1]);
return risk_ab < risk_ba;
}
int main()
{
uint32_t n;
scanf("%u", &n);
std::vector<std::array<int32_t, 2>> bull(n);
for (uint32_t i = 0; i < n; ++i) {
scanf("%u%u", &bull[i][0], &bull[i][1]);
}
std::sort(bull.begin(), bull.end(), &cmp);
int32_t risk_max = INT32_MIN;
int32_t weight_above = 0;
for(uint32_t i = 0; i < n; ++i){
risk_max = std::max(risk_max, weight_above - bull[i][1]);
weight_above += bull[i][0];
}
printf("%d\n", risk_max);
return 0;
}