【并没有传送门】
1.最大子序列之和2
Description
上次数学考试考了一道这样的题目:一个有n个整数的数字序列,其间有正负整数,现
在的任务是求出两段不相交的连续子序列,使这两段中的数字和最大。
Input
第一行,一个数n,表示整数的个数。
第二行n个数,描述这个序列。
n<=1000000
Output
仅一行,表示最大的数字和
Sample Input
10
1 2 -3 2 1 4 -3 2 1 2
Sample Output
12
//(取子序列 2, 1, 4 和 2, 1, 2,得到最大的和)
Sol
一开始对于这个两个不相交的区间真的是纠结了很久不知道怎么做【甚至是暴力】。最后一分钟竟然还想到了并查集。【什么东西
对于最大字段和其实都是没有问题的,但是想到要找两个竟然就懵了。其实只要枚举一个变量作为中间点,隔开两边分别找最大就行了。至于两边的最大,我们可以直接预处理——得到前缀和和后缀和,正反方向取最大,然后扫一遍看是直接取sum还是取前面的结果。
上代码。
#include<bits/stdc++.h>
#define maxn 1000005
using namespace std;
int n, a[maxn], sum1[maxn], sum2[maxn], ans1[maxn], ans2[maxn];
int ans = -(1 << 30);
int read()//快读优化30ms
{
register int x = 0, f = 1, ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x * f;
}
int main()
{
n = read();
for(int i = 1; i <= n; i++)
a[i] = read(), sum1[i] = max(sum1[i - 1] + a[i], a[i]), ans1[i] = max(ans1[i - 1], sum1[i]);//sum取最大,递推过去
for(int i = n; i > 0; i--)
sum2[i] = max(sum2[i + 1] + a[i], a[i]), ans2[i] = max(ans2[i + 1], sum2[i]);
//直接用a[ i ]是说可以从a[ i ]开始,ans用于调用。以上就是最大字段和的初始化
for(int i = 2; i < n; i++)//这里的区间大概是这么个意思就行,具体从哪里到哪里随意
ans = max(ans, ans1[i] + ans2[i + 1]);//从点i分开
printf("%d\n", ans);
return 0;
}
2.超级教主
最近自己单调队列学到自闭所以要梳理一下思路了QwQ
Description
LHX教主很能跳,因为Orz他的人太多了。教主跳需要消耗能量,每跳1米就会消耗1点能量,如果教主有很多能量就
能跳很高。教主为了收集能量,来到了一个神秘的地方,这个地方凡人是进不来的。在这里,教主的正上方每100
米处就有一个能量球(也就是这些能量球位于海拔100,200,300……米处),每个能量球所能提供的能量是不同
的,一共有N个能量球(也就是最后一个能量球在N×100米处)。教主为了想收集能量,想跳着吃完所有的能量球
。教主可以自由控制他每次跳的高度,接着他跳起把这个高度以下的能量球都吃了,他便能获得能量球内的能量,
接着吃到的能量球消失。教主不会轻功,教主不会二段跳,所以教主不能因新吃到的能量而变化此次跳跃的高度。
并且教主还是生活在地球上的,所以教主每次跳完都会掉下来。问教主若要吃完所有的能量球,最多还能保留多少
能量。
Input
第1行包含两个正整数N,M,表示了能量球的个数和LHX教主的初始能量。
第2行包含N个非负整数,从左到右第I个数字依次从下向上描述了位于I×100米位置能量球包含的能量,整数之间用空格隔开。
N≤2000000。
保证对于所有数据,教主都能吃到所有的能量球,并且能量球包含的能量之和不超过2^31-1
Output
仅包括一个非负整数,为教主吃完所有能量球后最多保留的能量。
Sample Input
3 200
200 200 200
Sample Output
400
Sol
第一反应是单调队列,然后是dp,然后是贪心。最后一个也没写出来直接爆炸。【噗】
因为是最后剩的最多的能量,所以dp肯定是没有问题的了,存变量只能用前缀和也是没有问题的。由此易得:
当时甚至是在想能不能在j的基础上继续往上跳,感觉不算二段但其实已经算了j处得到的能量所以算是二段跳了QwQ】
f[ j ]是算过了,sum是固定的,所以是否选择在j之后一步跳到这里就取决于减去跳到这么高的能量过后哪个剩的更多。并且同时要尽量减去的高度低,因为跳跃的次数明显是越少越好。所以这里就可以扯出单调队列了——维护一个单调递减的队列。至于到底是什么递减,就是上方黑体字了。
所以说又是一个单调队列维护dp的 水 题【dalao们是这么定义的】。个人觉得单调队列真的就像dp一样,想到了算法但写不出来,就是因为——你想多了。
下面是代码及详解——【当然也可以直接dp贪心,就不用开队列维护,一个变量维护下标就好了。代码量会少一两行。
#include<bits/stdc++.h>
#define maxn 2000005
using namespace std;
int n, m, sum[maxn], f[maxn], tmp;
int q[maxn], head = 0, tail = 1;
int read()//快读优化270ms
{
register int x = 0, f = 1, ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x * f;
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; i++)
{
tmp = read();
sum[i] += sum[i - 1] + tmp;
}
int j = 0;
f[0] = m;
for(int i = 1; i <= n; i++)
{
while(head <= tail && f[q[head]] < i * 100) head++;//
f[i] = f[q[head]] - sum[q[head]] + sum[i] - i * 100;//单调队列维护,直接操作
while(head <= tail && f[q[tail]] - sum[q[tail]] <= f[i] - sum[i]) tail--;公平比较之前的跳跃剩余
q[tail++] = i;//加上前面两个代空注释,这是单调队列套路操作
}
printf("%d", f[n]);
return 0;
}
又是一个我没想通到底单调队列维护什么单调甚至是贪心竟然都WA了的题。
3、Escape
Description
给出数字N(1<=N<=10000),X(1<=x<=1000),Y(1<=Y<=1000),代表有N个敌人分布一个X行Y列的矩阵上矩形的
行号从0到X-1,列号从0到Y-1再给出四个数字x1,y1,x2,y2,代表你要从点(x1,y1)移到(x2,y2)。在移动的过程中你
当然希望离敌人的距离的最小值最大化,现在请求出这个值最大可以为多少,以及在这个前提下你最少要走多少步
才可以回到目标点。注意这里距离的定义为两点的曼哈顿距离,即某两个点的坐标分为(a,b),(c,d)那么它们的距
离为|a-c|+|b-d|
Input
第一行给出数字N,X,Y
第二行给出x1,y1,x2,y2
下面将有N行,给出N个敌人所在的坐标
Output
在一行内输出你离敌人的距离及在这个距离的限制下,你回到目标点最少要移动多少步。
Sample Input
2 5 6
0 0 4 0
2 1
2 3
Sample Output
2 14
Sol
第三题暂时不会。至少当时直接懵了。
貌似是要二分答案然后bfs?
【继续自闭】
——End——