离散化

离散化
离散化是一个非常常用的技巧,它可以有效的降低时间复杂度。
其本质就是在我们所枚举的所有情况中找出我们所要操作的值

在我们遇到一个需要开辟很大空间的问题时,如果我们真的这样去做,我们就会发现这会造成时间超限等问题,但是实际上操作的个数却是比较少的,我们将这些分散的数字映射到一个较小的空间内,这个过程就叫做离散化
对于可离散化数据我们通常只考虑数据中的大小关系。
进行离散化之前有两大问题
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;	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值