基础算法(双指针,离散化,区间合并)

NO.1  双指针算法

模板:

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // 具体问题的逻辑
}
//常见问题分类:
//    (1) 对于一个序列,用两个指针维护一段区间
//    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

例题:最长连续不重复子序列

给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

输入格式

第一行包含整数 n。

第二行包含 n 个整数(均在 0∼1050∼105 范围内),表示整数序列。

输出格式

共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。

数据范围

1≤n≤10^5

输入样例:

5
1 2 2 3 5

输出样例:

3

 代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

const int N = 1e5 + 10;
using namespace std;
typedef pair PII;
int res = 0;
int a[N],s[N];
int main(){
	int n;
	cin >> n;
	for(int i = 0;i < n;i ++)
		cin >> a[i];
	for(int i = 0,j = 0;i < n;i ++){
		s[a[i]] ++;
		while(s[a[i]] > 1){
			s[a[j]] --;
			j ++;
            //一旦发现有某个数在s数组里面出现了两次,马上清除掉前面的记忆(也就是把前面所有的数从s数组里删掉,为了下一次求字符串的长度做准备,同时把指针j移动过来,最后的结果就是i和j移到了一起。并且i指针指的位置在s数组里还留有一个1,因为while的条件是s[a[i]]>1)
		}
		res = max(res,i - j + 1);
	}
	cout << res << endl;
	return 0;
}

NO.2  离散化

模板:

vector alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n
}

例题:区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是 00。

现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。

接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间  [l , r]  之间的所有数的和。

输入格式

第一行包含两个整数 n 和 m。

接下来 n 行,每行包含两个整数 x 和 c。

再接下来 m 行,每行包含两个整数 l 和 r。

输出格式

共 m 行,每行输出一个询问中所求的区间内数字和。

数据范围

−10^9 ≤ x ≤ 10^9,
1 ≤ n , m ≤ 10^5,
−10^9 ≤ l ≤ r ≤ 10^9,
−10000 ≤ c ≤ 10000

输入样例:

3 3
1 2
3 6
7 5
1 3
4 6
7 8

输出样例:

8
0
5

代码:

#include 
#include 
#include 

using namespace std;
const int N = 300010; //n次插入和m次查询相关数据量的上界
int n, m;
int a[N];//存储坐标插入的值
int s[N];//存储数组a的前缀和
vector alls;  //存储(所有与插入和查询有关的)坐标
vector> add, query; //存储插入和询问操作的数据

int find(int x) { //返回的是输入的坐标的离散化下标
    int l = 0, r = alls.size() - 1;
    while (l < r) {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        int x, c;
        scanf("%d%d", &x, &c);
        add.push_back({x, c});
        alls.push_back(x);
    }
    for (int i = 1; i <= m; i++) {
        int l , r;
        scanf("%d%d", &l, &r);
        query.push_back({l, r});
        alls.push_back(l);
        alls.push_back(r);
    }
   //排序,去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    //执行前n次插入操作
    for (auto item : add) {
        int x = find(item.first);
        a[x] += item.second;
    }
    //前缀和
    for (int i = 1; i <= alls.size(); i++) s[i] = s[i-1] + a[i];
    //处理后m次询问操作
    for (auto item : query) {
        int l = find(item.first);
        int r = find(item.second);
        printf("%d\n", s[r] - s[l-1]);
    }

    return 0;
}

NO.3 区间合并

模板:

// 将所有存在交集的区间合并
void merge(vector &segs)
{
    vector res;

    sort(segs.begin(), segs.end());

    int st = -2e9, ed = -2e9;
    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);

    if (st != -2e9) res.push_back({st, ed});

    segs = res;
}

题目:区间合并

给定 n 个区间 [l-i,r-i],要求合并所有有交集的区间。

注意如果在端点处相交,也算有交集。

输出合并完成后的区间个数。

例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,6][1,6]。

输入格式

第一行包含整数 n。

接下来 n 行,每行包含两个整数 l 和 r。

输出格式

共一行,包含一个整数,表示合并区间完成后的区间个数。

数据范围

1 ≤ n ≤ 100000,
−10^9 ≤ l-i ≤ r-i ≤ 10^9

输入样例:

5
1 2
2 4
5 6
7 8
7 9

输出样例:

3

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

const int N = 1e5 + 10;
using namespace std;
typedef pair PII;
int n;
vector segs;
void merge(vector &segs){
	vector res;
	sort(segs.begin(),segs.end());
	int st = -2e9, ed = -2e9;  //ed代表区间结尾,st代表区间开头
	for(auto seg : segs){
		if(ed < seg.first){  //情况1:两个区间无法合并
			if(st != -2e9) res.push_back({st,ed});  //区间1放进res数组
			st = seg.first,ed = seg.second;  //维护区间2
		}
		else ed = max(ed,seg.second);  //情况2:两个区间可以合并,且区间1不包含区间2,区间2不包含区间1
	}
	if(st != -2e9) res.push_back({st,ed});
	segs = res;
	
}
int main(){
	cin >> n;
	for(int i = 0;i < n;i ++){
		int l,r;
		cin >> l >> r;
		segs.push_back({l,r});
	}
	merge(segs);
	cout << segs.size() << endl;
	return 0;
}

你可能感兴趣的:(算法,数据结构)