P1716 双调序列
题目描述
电脑组的童鞋们经常玩一些智力 PK 小游戏,某月某日,发源于小朋友又发明了一种新的序列:双调序列,所谓的双调呢主要是满足如下条件描述:
假定有 个整数(都在 long int
范围内,即 ),双调序列的第一个数是 个整数中的最大数,第二个数是 个整数中的最小数,第三个数是 个数中的第二大数,第四个数是 个数中的第二小数……取过的数不能再取,依次类推,直到结束。
聪明的你听完描述就抿嘴笑了吧?那就请你用程序正确的帮他找出这 个数的双调序列。
输入格式
第一行为一个整数 。
接下来 行给出了题目中所述的 个整数,每行包含一个整数。
输出格式
有 行,每行为一个整数,是满足条件的双调序列。
样例
输入 #1
5
10
-1
3
3
-9
输出 #1
10
-9
3
-1
3
数据范围
对于 100% 的数据, 。
问题分析
题目中的 long int 实际上就是 int 的数据范围。双调序列实际上就是交替输出该序列的最值。
只需要使用 sort 排序一下,从两头输出即可(对撞指针 i < j)。
需要注意边界情况,当序列长度为偶数时,对撞指针恰好输出整个序列;当序列长度为奇数时,对撞指针指向同一个数(尚未输出)。
求解代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int a[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);
for (int i = 0, j = n - 1; i <= j; i++, j--)
{
if (i == j)
{
cout << a[i] << endl;
break;
}
cout << a[j] << endl;
cout << a[i] << endl;
}
return 0;
}
P1147 连续自然数和
问题描述
对一个给定的正整数 ,求出所有的连续的正整数段(每一段至少有两个数),这些连续的自然数段中的全部数之和为 。
例子:,所以从 到 的一个自然数段为 的一个解。
输入格式
包含一个整数的单独一行给出 的值 。
输出格式
每行两个正整数,给出一个满足条件的连续正整数段中的第一个数和最后一个数,两数之间用一个空格隔开,所有输出行的第一个按从小到大的升序排列,对于给定的输入数据,保证至少有一个解。
样例:
输入 #1
10000
输出 #1
18 142
297 328
388 412
1998 2002
问题分析
利用双指针(快慢指针)优化求子串(连续序列)的和。
记快慢指针分别为 。根据等差数列的知识可知,该自然数段的数和为 。单调性的证明:
1.当快指针前进一个单位,即 时,;
2.当慢指针前进一个单位,即 时,。
求解代码
#include<iostream>
#define s (i + j) * (i - j + 1) / 2
using namespace std;
int m;
int main()
{
cin >> m;
int sum = 0;
for (int i = 1, j = 1; i < m; i++)
{
while (j < i && s > m) j++;
if (s == m) cout << j << ' ' << i << endl;
}
return 0;
}
P1102 A-B数对
问题描述
给出一串正整数数列以及一个正整数 ,要求计算出所有满足 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 。
第二行, 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 的数对的个数。
样例
输入 #1
4 1
1 1 2 3
输出 #1
3
数据范围
对于 75% 的数据,。
对于 100% 的数据,。
问题分析
根据题目要求求出所有满足 的数对,我们可以先将原数组排序,然后可以发现:对于每个数 ,对应的数 是一段连续的区间(长度可以为1)。
考虑到排序后序列的有序性,对于每个数 ,找到的 对应的区间一定是单调不降,在求区间 的长度时,可以采用双指针来维护区间端点的下标。
求解代码
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int n, c;
int p[N];
int main()
{
cin >> n >> c;
for (int i = 0; i < n; i++) cin >> p[i];
sort(p, p + n);
// 对于每个A(p[i]),对应一段B(p[jl]~p[jr])
LL res = 0;
for (int i = 0, jl = 0, jr = 0; i < n; i++)
{
while (jr < i && p[i] - p[jr] >= c) jr++;
while (jl < i && p[i] - p[jl] > c) jl++;
if (jr - 1 >= jl && p[i] - p[jr - 1] == c && p[i] - p[jl] == c)
res += jr - jl;
}
cout << res << endl;
return 0;
}