股票买卖 II

题意:给定股票每天的价格a[],要求计算可以获得的最大利润(交易次数不限,只能买一股)

思考:贪心。若最优解有在第i天买,第j天卖,那么利润等同于连续在i...j 天买卖。所以只要a[i-1]<a[i],就把它算到利润中去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long LL;
int n,a[100010];
LL ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(i!=1&&a[i]>a[i-1]) ans+=a[i]-a[i-1];
}
cout<<ans;
return 0;
}

技巧总结:将最优解的一部分等价替换为一系列简单的操作。

货仓选址

题意:线段上N家商店,位置已给定,要求选取一个仓库,到所有商店的距离和最小

思考:其实就是小学奥数题。先猜将仓库建到中间,证明:推表达式,形式是一系列绝对值的和,将他们按照在仓库左边/在仓库右边分组,可以去掉绝对值。最后发现取中间时最小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <algorithm>

using namespace std;

const int N = 100005;

int n, res;
int a[N];

int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

sort(a, a + n);

for (int i = 0; i < n; i ++ ) res += abs(a[i] - a[n >> 1]);
printf("%d\n", res);

return 0;
}

技巧总结:数据范围1e5,大概率nlong n,不妨排个序。

糖果传递

题意:有 n个小朋友坐成一圈,每人有 a[i] 个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1。求使所有人获得均等糖果的最小代价。

思考:高中数学题。看数据范围,不太可能用dp,先试着用推导数学公式。

假设标号为i的小朋友开始有Ai颗糖果,Xi表示第i个小朋友给了第i-1个小朋友Xi颗糖果,如果Xi<0,说明第i-1个小朋友给了第i个小朋友Xi颗糖果,X1表示第一个小朋友给第n个小朋友的糖果数量。 所以最后的答案就是ans=|X1| + |X2| + |X3| + ……+ |Xn|。

按定义。有A1-X1+X2=ave。A2-X2+X3=ave……

最终,我们可以得到n个方程,一共有n个变量,但是因为从前n-1个方程可以推导出最后一个方程,所以实际上只有n-1个方程是有用的。尽管无法直接解出答案,但可以用X1表示出其他的Xi,那么本题就变成了单变量的极值问题。

对于第1个小朋友,A1-X1+X2=ave -> X2=ave-A1+X1 = X1-C1(假设C1=A1-ave,下面类似)
对于第2个小朋友,A2-X2+X3=ave -> X3=ave-A2+X2=2ave-A1-A2+X1=X1-C2 即C2=A1+A2-2ave=A2+C1-ave以此类推Ci的通式为Ci=Ai+C[i-1]-ave

我们希望Xi的绝对值之和尽量小,即|X1| + |X1-C1| + |X1-C2| + ……+ |X1-Cn-1|要尽量小。注意到|X1-Ci|的几何意义是数轴上的点X1到Ci的距离,所以这道题变成了上面货仓选址这题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,a[1000001],c[1000001],ave;ll sum;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
ave=sum/n;

for(int i=2;i<=n;i++)
c[i]=c[i-1]+a[i]-ave;
sort(c+1,c+n+1);



ll ans=0;
int mid=c[(n>>1)+1];
for(int i=1;i<=n;i++)
ans+=abs(c[i]-mid);
printf("%lld",ans);
return 0;
}

技巧总结:遇到这种题就要耐心推数学公式

雷达设备

题意:一个平面直角坐标系,雷达位于x轴上,监测范围是半径为d的圆,有若干小岛位于x轴上方,问需要多少雷达能监测所有小岛。

思考:计算雷达监测的范围较困难。我们计算监测某个小岛,雷达需要在的范围。对于每个小岛,都计算出这样一个区间。那么就转换成了经典的贪心问题:区间选点。先对右端点排个序,然后从左到右依次选未覆盖区间的右端点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef pair<double, double> PDD;
const int N = 1010;
const double eps = 1e-6, INF = 1e10;

int n, d;
PDD seg[N];

int main()
{
cin >> n >> d;

bool success = true;

for (int i = 0; i < n; i ++ )
{
int x, y;
cin >> x >> y;
if (y > d)
{
success = false;
break;
}
auto len = sqrt(d * d - y * y);
seg[i] = {x + len, x - len};
}

if (!success) puts("-1");
else
{
sort(seg, seg + n);
int res = 0;
double last = -INF;
for (int i = 0; i < n; i ++ )
{
if (seg[i].second > last + eps)
{
res ++ ;
last = seg[i].first;
}
}
cout << res << endl;
}
return 0;
}

技巧总结:逆向思考,区间选点贪心。

最大不相交区间数量

题意:给定 N 个闭区间 [ai,bi][ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。输出可选取区间的最大数量。

思考:最大不相交区间数==最少覆盖区间点数。因为如果几个区间能被同一个点覆盖,说明他们相交了,所以有几个点就是有几个不相交区间