推公式算法的实现

简介: 推公式算法的实现

农民约翰的N头奶牛(编号为1…N)计划逃跑并加入马戏团,为此它们决定练习表演杂技。

奶牛们不是非常有创意,只提出了一个杂技表演:

叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。

奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。

这N头奶牛中的每一头都有着自己的重量Wi以及自己的强壮程度Si。

一头牛只撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。

您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。

输入格式

第一行输入整数N,表示奶牛数量。

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

输出格式

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

数据范围

1≤N≤50000,

1≤Wi≤10,000,

1≤Si≤1,000,000,000

输入样例:

3

10 3

2 5

3 3

输出样例:

2

(贪心)

思路: 与国王游戏的贪心策略相似, 我们先分析每头牛的危险值 = 他前面牛的w(重量值)和 - 自身的s(强壮值),要使每头牛的危险值最小,这显然是与w 和 s同时相关,所以先 yy 出一种做法按 每头牛的w + s进行升序排序(题见多了可能就会有这种题感)。接下来进行数学分析证明:

牛 交换前 交换后

ii

∑j=1i−1wj−si

∑j=1i−1wj−si

∑j=1i−1wj+wi+1−si

∑j=1i−1wj+wi+1−si

i+1i+1

∑j=1iwj−si+1

∑j=1iwj−si+1

∑j=1i−1wj−si+1

∑j=1i−1wj−si+1

其他牛的危险值显然不变,所以分析交换前后这两头牛中最大的危险值即可。

将上述式子进行化简,每个式子减去 ∑i−1j=1wj∑j=1i−1wj得到如下式子

牛 交换前 交换后

ii

−si

−si

wi+1−si

wi+1−si

i+1i+1

wi−si+1

wi−si+1

−si+1

−si+1

由于s, w都是正数,wi−si+1>−si+1wi−si+1>−si+1 , wi+1−si>−siwi+1−si>−si

比较wi−si+1wi−si+1, wi+1−siwi+1−si即可

当wi−si+1>=wi+1−siwi−si+1>=wi+1−si,即 wi+si>=wi+1+si+1wi+si>=wi+1+si+1时, 交换后更优

当wi−si+1<wi+1−siwi−si+1<wi+1−si,即 wi+si<wi+1+si+1wi+si<wi+1+si+1时, 交换前更优

所以得到做法: 按每头牛的 w + s 进行排序, 当存在逆序时就进行交换(即升序排序),

然后根据题意算出每头牛的危险值记录其中的最大值即可

代码:

#include
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 5e4 + 5;
PII a[N];
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++ )
{
int x, y;
scanf(“%d %d”, &x, &y);
a[i].first = x + y;
a[i].second = y;
}
sort(a, a + n);
ll res = -1e18, sum = 0;
for(int i = 0; i < n; i ++ )
{
sum -= a[i].second;
res = max(res, sum);
sum += a[i].first;
}
cout << res << endl;
return 0;
}

底下的牛的强壮程度SS越大越好

上面牛的重量和WW越小越好(越重的放越下面)

=>

直觉是S[i]+W[i]越大的放越下面

证:

贪心得到的答案≥最优解(根据最优解定义:所有排序方案的最小值)

贪心得到的答案≤最优解

如果不是按S[i]+W[i]从小到大排序一定存在相邻i和i+1有wi+si>wi+1+si+1wi+si>wi+1+si+1

可以发现此时交换第ii头牛和第i+1i+1头牛不影响[0,i−1][0,i−1]和[i+2,n][i+2,n]的风险

变化 第ii个位置上的牛 第i+1i+1个位置上的牛

交换前

w1+…+w(i−1)−si

w1+…+w(i−1)−si

w1+…+w(i)−s(i+1)

w1+…+w(i)−s(i+1)

交换后

w1+…+w(i−1)−s(i+1)

w1+…+w(i−1)−s(i+1)

w1+…+w(i−1)+w(i+1)−s(i)

w1+…+w(i−1)+w(i+1)−s(i)

前i−1i−1项减去

变化 第ii个位置上的牛 第i+1i+1个位置上的牛

交换前

−si

−si

w(i)−s(i+1)

w(i)−s(i+1)

交换后

−s(i+1)

−s(i+1)

w(i+1)−s(i)

w(i+1)−s(i)

加上si+si+1si+si+1

变化 第ii个位置上的牛 第i+1i+1个位置上的牛

交换前

s(i+1)

s(i+1)

wi+si

wi+si

交换后

si

si

w(i+1)+s(i+1)

w(i+1)+s(i+1)

wi+si>siwi+si≥w(i+1)+s(i+1)wi+si≥max(si,w(i+1)+s(i+1))由wi+si>wi+1+si+1−>wi+si>si+1−>wi+si=max(s(i+1),wi+si)max(s(i+1),wi+si)≥max(si,w(i+1)+s(i+1))max(交换前)≥max(交换后)所有风险的最大值起码不会变大即只要wi+si不是严格递增,则我们可以把他们变成递增的并且所有风险的最大值不会变大

wi+si>siwi+si≥w(i+1)+s(i+1)wi+si≥max(si,w(i+1)+s(i+1))由wi+si>wi+1+si+1−>wi+si>si+1−>wi+si=max(s(i+1),wi+si)max(s(i+1),wi+si)≥max(si,w(i+1)+s(i+1))max(交换前)≥max(交换后)所有风险的最大值起码不会变大即只要wi+si不是严格递增,则我们可以把他们变成递增的并且所有风险的最大值不会变大

#include
#include
using namespace std;
typedef pair<int,int> PII;
const int N=50010;
#define x first
#define y second
int n;
PII cow[N];
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
int w,s;
cin >> w >> s;
cow[i] = {w+s,w};
}
sort(cow,cow+n);
int res = -1e9,sum=0;
for(int i=0;i<n;i++)
{
int s;
s = cow[i].x-cow[i].y;
res = max(res,sum-s);//风险 = 上面的重量-当前的承受
sum+=cow[i].y;
}
cout << res;
return 0;
}


相关文章
|
5月前
|
算法
算法题(8)
算法题(8)
24 4
|
5月前
|
算法 机器人
算法题(3)
算法题(3)
51 5
|
5月前
|
算法
算法题(2)
算法题(2)
34 3
|
7月前
|
存储 算法 网络安全
|
7月前
|
存储 传感器 编解码
|
算法
算法
一、算法 常见的图查找算法包括: 1. 深度优先搜索(DFS):从图中的一个节点开始,沿着一条路径一直深入直到无法再深入为止,然后回溯到上一个节点,继续深入其他路径,直到找到目标节点或遍历完所有节点。 2. 广度优先搜索(BFS):从图中的一个节点开始,先访问它的所有邻居节点,然后再依次访问邻居的邻居节点,直到找到目标节点或遍历完所有节点。 3. Dijkstra算法:用于在带权有向图中找到从一个节点到其他节点的最短路径。该算法通过不断更新节点的最短距离来逐步找到最短路径。 4. A*算法:类似于Dijkstra算法,但在计算最短路径时加入了启发式函数,用于估计目标节点的距离,从而加速搜索过程
404 0
|
存储 并行计算 算法
FlashAttention算法详解
这篇文章的目的是详细的解释Flash Attention,为什么要解释FlashAttention呢?因为FlashAttention 是一种重新排序注意力计算的算法,它无需任何近似即可加速注意力计算并减少内存占用。所以作为目前LLM的模型加速它是一个非常好的解决方案,本文介绍经典的V1版本,最新的V2做了其他优化我们这里暂时不介绍。因为V1版的FlashAttention号称可以提速5-10倍,所以我们来研究一下它到底是怎么实现的。
607 0
|
算法 索引
紫书之子集生成三种算法
紫书之子集生成三种算法
紫书之子集生成三种算法
拓展欧几里得算法
拓展欧几里得算法
103 0
|
算法
算法题:出现
题目: 给定 n 个自然数,求没有在这 n 个自然数中出现过的最小的自然数是多少。
126 0