目录
AcWing 1055. 股票买卖 II
给定一个长度为 N 的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入格式
第一行包含整数 N,表示数组长度。
第二行包含 N 个不大于 10000 的正整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1≤N≤105
输入样例1:
6
7 1 5 3 6 4
输出样例1:
7
输入样例2:
5
1 2 3 4 5
输出样例2:
4
输入样例3:
5
7 6 4 3 1
输出样例3:
0
样例解释
样例1:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。共得利润 4+3 = 7。
样例2:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
样例3:在这种情况下, 不进行任何交易, 所以最大利润为 0。
思路:
按照样例一来分析:
在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。共得利润 4+3 = 7。
即我的收益等于我每次获取的股票的涨幅。
#include<iostream>
using namespace std;
int n,sum;
int a[100010];
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n-1;i++)
{
int d=a[i+1]-a[i];
if(d>0)
sum+=d;
}
cout<<sum<<endl;
return 0;
}
AcWing 1235. 付账问题
几个人一起出去吃饭是常有的事。
但在结帐的时候,常常会出现一些争执。
现在有 n 个人出去吃饭,他们总共消费了 SS 元。
其中第 i 个人带了 ai 元。
幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?
为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。
这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 1 分钱的整数倍。
你需要输出最小的标准差是多少。
标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。
形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 :
输入格式
第一行包含两个整数 n、Sn、S;
第二行包含 n 个非负整数 a1, …, an。
输出格式
输出最小的标准差,四舍五入保留 4 位小数。
数据范围
1≤n≤5×105,
0≤ai,S≤109,
输入样例1:
5 2333
666 666 666 666 666
输出样例1:
0.0000
输入样例2:
10 30
2 1 4 7 4 8 3 6 4 7
输出样例2:
0.7928
思路:
首先我们要知道标准差表示的是数据的波动程度,其值越大波动越大。要使得标准差小,我们就要尽可能使得数据都比较接近平均值。那么这题贪心策略应该是这样的:首先算出平均值s/ns/n,把数据从小到大排序,如果某个人的钱低于该值,那么他一定是将钱全部支付,然后其余不够的其他人平摊。但是,由于之前那个人钱不够,那么就会导致剩下人支付的平均值会增大,所以在这个平摊过程中很有可能存在某个人钱又低于这个平均值,又需要剩下的人平摊。如此反复,直到支付完成。
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n;
int a[500010];
int main()
{
double s;
cin>>n>>s;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
double res=0,avg=s/n;
for(int i=0;i<n;i++)
{
double cur=s/(n-i); //这个人需要付的钱
if(a[i]<cur) cur=a[i]; //钱不够就全付
res+=(cur-avg)*(cur-avg);
s-=cur; //需要付的总金额减少
}
printf("%.4f",sqrt(res/n));
return 0;
}
AcWing 1239. 乘积最大
输入样例1:
5 3
-100000
-10000
2
100000
10000
输出样例1:
999100009
输入样例2:
5 3
-100000
-100000
-2
-100000
-100000
输出样例2:
-999999829
分析:
k 如果是偶数的话,选出来的结果一定是非负数:
(1) # 负数的个数是偶数个的话,负负得正,那么一定是非负数
(2) # 负数的个数如果是奇数个的话,那么我们就只选偶数个绝对值最大的负数or不选负数
k 如果是奇数个的话:
(1)# 所有的数字如果都是负数,那么选出来的结果也一定都是负数
(2)# 否则的话,则一定至少有 1个非负数, 那么我们将最大的数取出来, 此时要选的个数是 k--, k-- 是偶数,那么就又转化为是偶数的情况思考
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10,mod=1000000009;
int n,k;
int a[N];
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n); //核心
int res=1;
int l=0,r=n-1;
int sign=1;
if(k%2==1)
{
res=a[r]; //如果是奇数,取最大值
r--;
k--; //数目变成偶数,使之可用两边同时对比的操作
if(res<0) sign=-1; //得出全是负数且奇
}
while(k)
{
LL x=(LL)a[l]*a[l+1]; //如果是两负值,相乘得正可能最大
LL y=(LL)a[r-1]*a[r];
if(x*sign>y*sign) //如果sign为-1,说明答案必然是负数,选出小的止损
{
res=x%mod*res%mod; //x最大是 10^10,必须先mod,不然爆long long
l+=2;
}
else
{
res=y%mod*res%mod;
r-=2;
}
k-=2;
}
cout<<res;
return 0;
}
AcWing 1247. 后缀表达式
给定 N 个加号、M 个减号以及 N+M+1 个整数 A1,A2,⋅⋅⋅,AN+M+1,小明想知道在所有由这 N 个加号、M 个减号以及 N+M+1 个整数凑出的合法的后缀表达式中,结果最大的是哪一个?
请你输出这个最大的结果。
例如使用 123+−,则 “23+1−” 这个后缀表达式结果是 4,是最大的。
输入格式
第一行包含两个整数 N 和 M。
第二行包含 N+M+1 个整数 A1,A2,⋅⋅⋅,AN+M+1。
输出格式
输出一个整数,代表答案。
数据范围
0≤N,M≤10E5,
−109≤Ai≤10E9
输入样例:
1 1
1 2 3
输出样例:
4
(网上找的大佬图解)
其实就是有n个加号,m个减号,n+m+1个数,可以加括号,问组成表达式的最大值
可以发现,现在如果我想加一个数的话,给它一个加号,放在括号外面,也可以给它一个减号,放在括号里面;减一个数同理。换句话说,只要用一个减号,一个最大值,一个最小值,其他数我想加就加,想减就减。那么为了使结果最大,我加上正数,减去负数,就是直接加上所有剩下数的绝对值,那么就解决了。//由上图式子b-(a-c-d-e)-->b-a+c+d+e,m>0就至少减一个数
其中,当m为0,也就是没有负数时,我们不需要实施反转操作,所有的操作都为+,此时我们只需增加所有的数字即可
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 200010;
int n, m;
int a[N];
int main()
{
scanf("%d%d", &n, &m);
int k = n + m + 1;
for (int i = 0; i < k; i ++ ) scanf("%d", &a[i]);
LL res = 0;
if (!m)
{
for (int i = 0; i < k; i ++ ) res += a[i];
}
else
{
sort(a, a + k); // 也可以不排序,找出最大值和最小值即可
res = a[k - 1] - a[0];
for (int i = 1; i < k - 1; i ++ ) res += abs(a[i]);
}
printf("%lld\n", res);
return 0;
}
P1969 [NOIP2013 提高组] 积木大赛
这道题只要计算h[i]和h[i+1]两组的高度差即可:
- h[i]<h[i+1],即左边的一组比右边的矮,当高度满足左面时,右边的还差一些,那么ans加上两堆的高度差;
- h[i]>=h[i+1],即左边的一组比右边的高,当高度满足左面时,右边的也一定满足了,所以不需要增加ans。
#include <bits/stdc++.h>//热爱万能头
using namespace std;
int n,h[100050],ans;//ans为摆放次数
int main(){
cin>>n;
for(int i=1; i<=n; i++) cin>>h[i];
for(int i=1; i<=n; i++) if(h[i]>h[i-1]) ans+=h[i]-h[i-1];//代码核心
cout<<ans<<endl;
return 0;
}