离散化
离散化是一个非常常用的技巧,它可以有效的降低时间复杂度。
其本质就是在我们所枚举的所有情况中找出我们所要操作的值
在我们遇到一个需要开辟很大空间的问题时,如果我们真的这样去做,我们就会发现这会造成时间超限等问题,但是实际上操作的个数却是比较少的,我们将这些分散的数字映射到一个较小的空间内,这个过程就叫做离散化。
对于可离散化数据我们通常只考虑数据中的大小关系。
进行离散化之前有两大问题
1.原数组中的重复元素(去重)
2.离散过后的数组下标(二分查找)
进行去重我们可以选择C++中的unique
unique的使用方法:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int a[11]={1,1,1,8,7,4,4,1,1,9,3};
vector<int> b;
//1 1 4 3 4 4 5
b.push_back(1);
b.push_back(1);
b.push_back(4);
b.push_back(3);
b.push_back(4);
b.push_back(4);
b.push_back(5);
sort(a,a+11);
sort(b.begin(),b.end());
//unique只是将相邻的相同元素放在后面,所以要先进行排序
/*unique相当一个函数
他先把重复的元素放在后面
然后返回第一个重复元素的首地址
普通数组的去重后元素个数的求法
int n=0;
n=unique(a,a+11)-a; n表示去重后的size
动态数组的去重后元素个数的求法
int m=0;
m=unique(b.begin(),b.end())-b.begin();
*/
b.erase(unique(b.begin(),b.end()),b.end());
//这一步表示去掉重复元素
for(auto it=b.begin();it!=b.end();it++){
cout<<*it<<endl;
}
return 0;
}
接下来就是二分查找
我们可以使用lower_bound函数,直接查到每一个的位置
例题:
离散化+差分
There are N light bulbs indexed from 0 to N−1. Initially, all of them are off.
A FLIP operation switches the state of a contiguous subset of bulbs. FLIP(L,R)
means to flip all bulbs x such that L≤x≤R. So for example, FLIP(3,5) means to flip bulbs 3 , 4 and 5, and FLIP(5,5) means to flip bulb 5.
Given the value of N and a sequence of M flips, count the number of light bulbs that will be on at the end state.
InputFile
The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts with a line containing two integers N and M, the number of light bulbs and the number of operations, respectively. Then, there are M more lines, the i-th of which contains the two integers Li and Ri, indicating that the i-th operation would like to flip all the bulbs from Li to Ri , inclusive.
1≤T≤1000
1≤N≤1061≤M≤10000≤Li≤Ri≤N−1
OutputFile
For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the number of light bulbs that will be on at the end state, as described above.
有N个灯泡从0索引到N−1。一开始,它们都是关闭的。
翻转操作切换相邻灯泡子集的状态。翻转(左,右)
表示翻转所有灯泡x,使L≤x≤R。例如,FLIP(3,5)表示翻转灯泡3,4,5,FLIP(5,5)表示翻转灯泡5。
给定N的值和一个M次翻转的序列,计算有多少个灯泡将在结束状态。
InputFile
输入的第一行给出了测试用例的数量,T。接下来是测试用例。每个测试用例都从包含两个整数N和M的一行开始,这两个整数分别是灯泡数和操作数。然后,还有M行,第i行包含两个整数Li和Ri,表示第i行操作将所有灯泡从Li翻转到Ri(包括Ri)。
1≤T≤1000
1≤N≤1061≤≤10000李≤≤Ri≤N−1
OutputFile
对于每个测试用例,输出一行包含用例#x: y,其中x是测试用例号(从1开始),y是在结束状态时将亮起的灯泡数,如上所述。
(这道题如果对N进行操作会造成时间超限,因此我们选择对M进行操作)
在这里我们只考虑了我们所进行操作的灯泡
例如:
6 3
1 1
2 3
3 4
这组数据得到的pair数组为(1,1)(2,-1)(2,1)(3,1)(4.-1)(5,-1)
p[i].first =1 2 2 3 4 5
p[i].second=1 -1 1 1 -1 -1
sum=1 0 1 2 1 0
ANS=1 2 3
ans=1 1 2 2 3 3
/*sum:这个数(p[i].first)到下一个数是奇数还是偶数,
奇数的话变化了奇数次 ,就说明这个数到下个数变化了奇数次,这个数到下个数灯都是开着的,
加上灯的个数 。变化偶数次都是关的。
*/
提交的代码如下:
#include<iostream>
#include<map>
//调用pair可以使用map头文件
#include<algorithm>
using namespace std;
pair< int,int > p[2020];
int main()
{
int i,t,n,m,k=1;
cin>>t;
while(t--){
cin>>n>>m;
int L,R,x=0;
for(i=0;i<m;i++){
scanf("%d %d",&L,&R);
p[x++]=make_pair(L,1);
p[x++]=make_pair(R+1,-1);
}
sort(p,p+x);//对p[i].first进行排序
int sum=0,ans=0;
for(i=0;i<x;i++){
sum+=p[i].second;
if(sum%2!=0)
ans+=p[i+1].first-p[i].first;
}
cout<<"Case #"<<k<<": "<<ans<<endl;
k++;
}
return 0;
}
如何理解该代码:
这里有数据可以自己测试
2
10 2
2 6
4 8
6 3
1 1
2 3
3 4
答案:
Case #1: 4
Case #2: 3
#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
pair< int,int > p[2020];
int main()
{
int i,t,n,m,k=1;
cin>>t;
while(t--){
cin>>n>>m;
int L,R,x=0;
for(i=0;i<m;i++){
scanf("%d %d",&L,&R);
p[x++]=make_pair(L,1);
p[x++]=make_pair(R+1,-1);
}
sort(p,p+x);
int sum=0,ans=0;
for(i=0;i<x;i++){
sum+=p[i].second;
cout<<"sum:"<<sum<<" ";
if(sum%2!=0)
{
ans+=p[i+1].first-p[i].first;
cout<<"ANS:"<<ans<<" ";
}cout<<"ans:"<<ans<<" ";
}
cout<<"Case #"<<k<<": "<<ans<<endl;
k++;
}
return 0;
}