离散化思想——只处理有效数据的优化思想
- 什么是离散化
- 离散化题目——校门外的树(超强版,1e9)
- 题目描述
- 输入格式
- 输出格式
- 样例
- 样例输入
- 样例输出
- 提示
- 思路分析
- 朴素做法
- 离散化!!
- 代码分析
- 数组
- 循环
什么是离散化
离散化思想可以理解为“只处理有效数据”的一种思想,对于问题的模型进行结构重建基本参考坐标是离散化代码实现的常见方法。对于数值范围巨大而实际有效点较少的问题可以采用离散化转化为更容易实现的问题求解。
你可以想象一下,如果你有
N
N
N根很长的绳子,你把它们交叉着叠在一起(有重叠部分也有没有绳子的部分),已知每根绳子的头和尾,你想知道这些绳子的覆盖长度,那你只能慢慢的量。
例如下面的数据:
7
-506 870
1125 1311
870 874
1653 1723
0 236
236 999
1442 1722
有什么简单的方法吗?
当我们将它门转换到数轴上,题目马上变得简单:
这样我们就可以计算出每一段的长度,再将每一段的长都加起来即可,事实上就是转换成了下图:
这样,我们求绳子的覆盖长度就可以很快的利用数学得出结果!
离散化题目——校门外的树(超强版,1e9)
题目描述
校门外马路上本来从编号
0
0
0 到
L
L
L,每一编号的位置都有一棵树。有砍树者每次从编号
A
A
A 到
B
B
B 处连续砍掉每一棵树,就连树苗也不放过(记 0 A B
,含
A
A
A 和
B
B
B);幸运的是还有植树者每次从编号
C
C
C 到
D
D
D 中凡是空穴(树被砍且还没种上树苗或树苗又被砍掉)的地方都补种上树苗(记 1 C D
,含
C
C
C 和
D
D
D);问最终校门外留下的树苗多少棵?植树者种上又被砍掉的树苗有多少棵?
输入格式
第一行,两个正整数 L L L 和 N N N,表示校园外原来有 L + 1 L + 1 L+1 棵树,并有 N N N 次砍树或种树的操作。
以下 N N N 行,每行三个整数,表示砍树或植树的标记和范围。
输出格式
共两行。第一行校门外留下的树苗数目,第二行种上又被拔掉的树苗数目。
样例
样例输入
10 3
0 2 6
1 1 8
0 5 7
样例输出
3
2
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ L ≤ 1 0 9 1 \le L \le 10^9 1≤L≤109, 1 ≤ N ≤ 100 1 \le N \le 100 1≤N≤100。
思路分析
在程序中,如何解决这个问题呢(不含负数)?
朴素做法
开一个数组代表数轴,每次输入进来一个绳子是就讲绳子覆盖的地方标记为 1 1 1,其它为 0 0 0。
然后从 0 0 0到总长度枚举,如果是 1 1 1,那么答案就增加 1 1 1。
代码异常简单:
#include <iostream>
using namespace std;
int i,j,k,m,n,l,sum=0;
int a[1000000005],q,z;
int main() {
cin>>l>>m;
for(i=0; i<=l; i++)
a[i]=0;
for(i=1; i<=m; i++) {
cin >>q >>z;
for(j=q; j<=z; j++)
a[j]=1;
}
for(i=0; i<=l; i++)
sum+=a[i];
cout <<l-sum+1;
return 0;
}
但是当你看到题目数据时,这种做法就只能放弃,因为你根本不可能开那么大的数组,并且还会超时!
那么该怎么办?
离散化!!
这题明细是用离散化做的!!
现将每个数进行排序,然后再处理。
#include <bits/stdc++.h>
using namespace std;
const int N=300;
priority_queue <int,vector<int>,greater<int> >q;
int l,m,kk,ans,a[N],b[N],w[N],p[N];
map<int,int> t;
int main() {
cin >>l >>m;
for (int i=1; i<=m; i++) {
cin >>a[i] >>b[i];
q.push(a[i]);
q.push(b[i]);
}
p[++kk]=q.top();
q.pop();
for (int i=2; i<=2*m; i++) {
if (p[kk-1]!=q.top())
p[++kk]=q.top();
p[kk];
t[q.top()]=kk;
q.pop();
}
for (int i=1; i<=m; i++) {
int x=t[a[i]];
int y=t[b[i]];
for (int j=x; j<y; j++) {
w[j]=1;
}
}
int s=0,e=0;
for (int i=1; i<=2*m; i++) {
if (w[i]==1) {
if (s==0) {
s=i;
e=i;
} else
e++;
}
if (w[i]==0) {
if (e>0) {
ans+=p[e+1]-p[s]+1;
e=0;
s=0;
}
}
}
cout <<l-ans+1;
return 0;
}
代码分析
数组
- q q q这个优先队列用来排序
- a a a数组和 b b b数组用来存储输入的数。
- w w w表示当前段有没有被砍
- p p p为排完序的数组
- t t t这个 m a p map map类型的东东用来快速找到当前数所对应的位置
循环
- 第一个:输入,并将数值存进优先队列
- 第二个:将排序后的数值存入 p p p数组, t t t数组记录当前数在有序数列中的位置
- 第三个:标记当前段有没有被覆盖
- 第四个:累加被覆盖的每一段的数值