算法基础—奶牛玩杂技(贪心算法)

文章讨论了如何通过计算奶牛的重量、强壮度和风险值来确定其在杂技表演中的最佳排列,以最大化安全度。
摘要由CSDN通过智能技术生成

AcWing 125. 耍杂技的牛
农民约翰的 N N N 头奶牛(编号为 1.. N 1..N 1..N)计划逃跑并加入马戏团,为此它们决定练习表演杂技。
奶牛们不是非常有创意,只提出了一个杂技表演:
叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。
奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。
N N N头奶牛中的每一头都有着自己的重量 W i Wi Wi以及自己的强壮程度 S i Si Si
一头牛支撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。
您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。

输入格式:
第一行输入整数 N N N,表示奶牛数量。
接下来 N N N行,每行输入两个整数,表示牛的重量和强壮程度,第 i i i行表示第 i i i头牛的重量 W i Wi Wi 以及它的强壮程度 S i Si Si

输出格式:
输出一个整数,表示最大风险值的最小可能值。

贪心策略进行猜想:
根据题意,我们可以得出危险值的公式如下:
每头牛的危险值 D = 他前面牛的 W ( 重量值 ) 之和 − 自身的 S ( 强壮值 ) 每头牛的危险值D = 他前面牛的W(重量值)之和 - 自身的S(强壮值) 每头牛的危险值D=他前面牛的W(重量值)之和自身的S(强壮值)
D = W − S D=W-S D=WS
由此,我们根据贪心思想,首先应该就会想到,如果要所有奶牛的风险值中的最大值尽可能的小。那么就应该把 W W W重量越重的越往底部堆叠;并且 S S S越强壮的越堆叠在下方。因此,猜想 W + S W+S W+S的值越大的应该越堆叠在下方。

分析证明:
在这里插入图片描述

如图所示,随机一种堆叠方式,那么何时我们才需要任意i和i+1的交换位置呢? ,那自然是任意位置 i i i i + 1 i+1 i+1交换之后,可以降低这两个位置中风险值D的最大值的情况下,才会进行交换。

由图中可知交换 i 和 i + 1 i和i+1 ii+1的位置并不会影响其他任何位置的危险值。此时已经求出交换 i 和 i + 1 i和i+1 ii+1的位置前后的危险值 D D D已经求出,为了方便比较,此处将交换前后 D D D中公共值都减去相同 ∑ j = 1 i − 1 W j \sum_{j=1}^{i-1} W_j j=1i1Wj

此时,只需要比较交换前后的值简化值即可。
交换前: i 对应: − S i ( 1 ) i对应: \quad -S_i \quad (1) i对应:Si(1)
i + 1 对应: W i − S i + 1 ( 2 ) i+1对应:\quad W_i-S_{i+1}\quad (2) i+1对应:WiSi+1(2)
交换后: i 对应: W i + 1 − S i ( 3 ) i对应: \quad W_{i+1}-S_i\quad (3) i对应:Wi+1Si(3)
i + 1 对应: − S i + 1 ( 4 ) i+1对应:\quad-S_{i+1} \quad (4) i+1对应:Si+1(4)

达到降低这两个位置中风险值D的最大值的效果,才会进行交换。即满足以下公式才进行交换
m a x [ ( 1 ) , ( 2 ) ] < m a x [ ( 3 ) , ( 4 ) ] max[(1),(2)]<max[(3),(4)] max[(1),(2)]<max[(3),(4)]
由于任意 W W W S S S都为正数很明显可以看出, W i + 1 − S i > − S i 和 W i − S i + 1 > − S i + 1 W_{i+1}-S_{i}> -S_i \quad和\quad W_i-S_{i+1}>-S_{i+1} Wi+1Si>SiWiSi+1>Si+1

故只需比较 W i − S i + 1 W_i-S_{i+1} WiSi+1 W i + 1 − S i W_{i+1}-S_i Wi+1Si的大小即可。因为无外乎两种情况,一种是最大值有变大,另一种是最大值变小。

如果 W i − S i + 1 > W i + 1 − S i W_i-S_{i+1}>W_{i+1}-S_i WiSi+1>Wi+1Si,交换前后危险值的最大值 m a x ( D i , D i + 1 ) max(D_i,D_{i+1}) max(DiDi+1)必然是变小了。因为 W i − S i + 1 > W i + 1 − S i W_i-S_{i+1}>W_{i+1}-S_i WiSi+1>Wi+1Si成立且 W i − S i + 1 > − S i + 1 W_i-S_{i+1}>-S_{i+1} WiSi+1>Si+1,所以 W i − S i + 1 W_i-S_{i+1} WiSi+1大于交换后的所有值,即 W i − S i + 1 W_i-S_{i+1} WiSi+1大于交换后的最大值。此时,无论交换前 W i − S i + 1 W_i-S_{i+1} WiSi+1是最大值还是 − S i -S_i Si是最大值,都可以得出交换前的最大值大于交换后的最大值,即进行交换后,危险值的最大值 m a x ( D i , D i + 1 ) max(D_i,D_{i+1}) max(DiDi+1)降低了。

如果 W i − S i + 1 < W i + 1 − S i W_i-S_{i+1}<W_{i+1}-S_i WiSi+1<Wi+1Si,交换前后危险值的最大值 m a x ( D i , D i + 1 ) max(D_i,D_{i+1}) max(DiDi+1)必然是变大了。因为 W i − S i + 1 < W i + 1 − S i W_i-S_{i+1}<W_{i+1}-S_i WiSi+1<Wi+1Si成立且 − S i < W i + 1 − S i -S_i<W_{i+1}-S_{i} Si<Wi+1Si,所以 W i + 1 − S i W_{i+1}-S_{i} Wi+1Si大于交换前的所有值,即 W i + 1 − S i W_{i+1}-S_{i} Wi+1Si大于交换前的最大值。此时,无论交换后 W i + 1 − S i W_{i+1}-S_{i} Wi+1Si是最大值还是 − S i + 1 -S_{i+1} Si+1是最大值,都可以得出交换后的最大值大于交换前的最大值,即进行交换后,危险值的最大值 m a x ( D i , D i + 1 ) max(D_i,D_{i+1}) max(DiDi+1)提高了。

此时交换前后,危险值的最大值的比较问题已经解决。
回归到正题。那么何时我们才需要任意i和i+1的交换位置呢?,当两个位置中风险值D的最大值降低就进行交换。即需要满足 W i − S i + 1 > W i + 1 − S i W_i-S_{i+1}>W_{i+1}-S_i WiSi+1>Wi+1Si才进行交换。最终可得公式
W i + S i > W i + 1 + S i + 1 W_i+S_i>W_{i+1}+S_{i+1} Wi+Si>Wi+1+Si+1
只有满足上述公式时,才能保证任意i和i+1位置的奶牛交换后,危险值的最大值降低。以图中堆叠的视角来看,就是只有堆叠在上方的重量 W W W和强壮值 S S S大于堆叠在下方重量 W W W和强壮值 S S S时,才需要向下交换位置,从而一步一步实现危险值的最大值的最小情况,否则的话说明堆叠的情况已经是危险值最大值的最小情况了。

AC代码
经过上述分析后,得出规律W+S越大,越应该堆叠在下面是正确。由此得到以下AC代码:

#include<iostream>
#include<algorithm>
#include <climits>
using namespace std;

const int N = 50010;

struct COW
{
	int w, s;
	bool operator<(const COW& c)const {
		return w + s < c.w + c.s;
	}
}cow[N];


int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) cin >> cow[i].w >> cow[i].s;
	//升序排序
	sort(cow, cow + n);
	//结果可能是负值,初始化为int最小值
	int res = INT_MIN;
	//记录某个奶牛的上方的累积重量
	int weights = 0;
	for (int i = 0; i < n; i++) {
	
		res = max(res, weights - cow[i].s);
		
		weights += cow[i].w;
	}
	cout << res << endl;
	return 0;


}
  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值