洛谷—双指针

P1716 双调序列

题目描述

电脑组的童鞋们经常玩一些智力 PK 小游戏,某月某日,发源于小朋友又发明了一种新的序列:双调序列,所谓的双调呢主要是满足如下条件描述:

假定有 n(n \leq 1000) 个整数(都在 long int 范围内,即 -2147483648\sim 2147483647 ),双调序列的第一个数是 n 个整数中的最大数,第二个数是 n 个整数中的最小数,第三个数是 n 个数中的第二大数,第四个数是 n 个数中的第二小数……取过的数不能再取,依次类推,直到结束。

聪明的你听完描述就抿嘴笑了吧?那就请你用程序正确的帮他找出这 n 个数的双调序列。

输入格式

第一行为一个整数 n 。

接下来 n 行给出了题目中所述的 n 个整数,每行包含一个整数。

输出格式

有 n 行,每行为一个整数,是满足条件的双调序列。

样例

输入 #1

5

10

-1

3

3

-9

输出 #1

10

-9

3

-1

3

数据范围

对于 100% 的数据,1 \leq n \leq 1000 。

问题分析

题目中的 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 连续自然数和

问题描述M

对一个给定的正整数 M ,求出所有的连续的正整数段(每一段至少有两个数),这些连续的自然数段中的全部数之和为 M 。

例子:1998+1999+2000+2001+2002=10000,所以从 1998 到 2002 的一个自然数段为 M = 10000 的一个解。

输入格式

包含一个整数的单独一行给出 M 的值 (10 \leq M \leq 2000000) 。

输出格式

每行两个正整数,给出一个满足条件的连续正整数段中的第一个数和最后一个数,两数之间用一个空格隔开,所有输出行的第一个按从小到大的升序排列,对于给定的输入数据,保证至少有一个解。

样例:

输入 #1

10000

输出 #1

18 142

297 328

388 412

1998 2002

问题分析

利用双指针(快慢指针)优化求子串(连续序列)的和。

记快慢指针分别为 i,j 。根据等差数列的知识可知,该自然数段的数和为 s = \frac{(i+j)(i-j+1)}{2} 。单调性的证明:

1.当快指针前进一个单位,即 i=i+1 时,\Delta s=s'-s=i+1

2.当慢指针前进一个单位,即 j=j+1 时,\Delta s=s'-s=-j

求解代码

#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数对

问题描述

给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A-B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个正整数 N,C

第二行,N 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 A-B=C 的数对的个数。

样例

输入 #1

4 1

1 1 2 3

输出 #1

3

数据范围

对于 75% 的数据,1 \leq N \leq 2000

对于 100% 的数据,1 \leq N \leq 2*10^{5},0 \leq a_{i} < 2^{30},1 \leq C < 2^{30}

问题分析

根据题目要求求出所有满足 A-B=C 的数对,我们可以先将原数组排序,然后可以发现:对于每个数 A,对应的数 B 是一段连续的区间(长度可以为1)

考虑到排序后序列的有序性,对于每个数 A,找到的 B 对应的区间一定是单调不降,在求区间 B 的长度时,可以采用双指针来维护区间端点的下标

求解代码

#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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值