电子学会六级-数据结构-离散化

电子学会六级-数据结构-离散化
离散化
离散化是一种数组处理编程技巧
有些数据因为本身很大或者类型不支持,自身无法作为数组的下标来方便地处理,而影响最终结果的只有元素之间的相对大小关系时,我们可以将原来的数据按照从大到小编号来处理问题,即离散化

用来离散化的可以是大整数、浮点数、字符串
离散化目的是将较大区间的个体映射到较小的区间中,提升空间效率,常用来求当前位置的数在源序列的相对位置
https://www.bilibili.com/video/BV1E3411W7G5
校门外的树
http://noi.openjudge.cn/ch0106/06/

#include<bits/stdc++.h>
using namespace std;

int main(){
	int a[10001];
	int l,m,x,y,s=0;
	scanf("%d%d",&l,&m);
	for(int i=0;i<=l;i++){// 0-l包括l+1种树位置 
		a[i]=1;//都赋值为1表示都种树 
	}
	for(int i=1;i<=m;i++){//m段修地铁 
		scanf("%d%d",&x,&y);//每段起始 
		for(int j=x;j<=y;j++){//每段起始位置包括中间数都不能种树 
			a[j]=0;//不能种树的地方设置为0 可能有重复的多次设置为0 
		}
	}
	for(int i=0;i<=l;i++){// 0-l包括l+1种树位置 
		if(a[i]==1) s++; //可以种树的位置累加到s 
	}
	printf("%d",s);
	return 0;
}

火烧赤壁
https://www.luogu.com.cn/problem/P1496

二分实现离散化

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=20050;
bool vis[maxn*2];
//len 存储p数组的长度
int n,len;
ll ans=0;
ll l[maxn];//存每艘船的左边坐标,将其离散化之后再存回l中 
ll r[maxn];//同l,但是存右边坐标 
ll p[maxn*2];//算法分析2中的p数组

/*
	二分 在离散后的p数组中 查找v在p中的下标 
*/
int search(int x,int y,ll v){
	int m;
	while(x<y){
		m=x+(y-x)/2;
		if(p[m]==v) return m;
		else if(p[m]>v) y=m;
		else x=m+1;
	}
	return -1;
}

int main(){
	cin>>n;
	for(int i=0;i<n;i++){//输入n条着火信息 
		cin>>l[i];//一条着火位置起点
		cin>>r[i];//一条着火位置终点
		p[len++]=l[i];//起点存入p数组 
		p[len++]=r[i];//终点存入p数组 
	}
	sort(p,p+len);//p数组排序 
	
	for(int i=0;i<n;i++){//离散化后的坐标 
		l[i]=search(0,len,l[i]);//p数组中找l[i]对应的位置放入l[i]
		r[i]=search(0,len,r[i]);//p数组中找r[i]对应的位置放入r[i]
		for(int j=l[i];j<r[i];j++)//l[i]~r[i]所有在p中的下标 左闭右开赋值 
			vis[j]=true;//赋值vis[] true 
	}
	
	for(int i=0;i<len;i++){//循环查看vis的连续true数 
		if(vis[i]) ans+=p[i+1]-p[i];//由于左闭右开赋值 区间长度p[i+1]-p[i] 
	}
	
	cout<<ans;
	return 0;
}

二分实现离散化--连续区间长度合并累加

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=20050;
bool vis[maxn*2];
//len 存储p数组的长度
int n,len;
ll ans=0;
ll l[maxn];//存每艘船的左边坐标,将其离散化之后再存回l中 
ll r[maxn];//同l,但是存右边坐标 
ll p[maxn*2];//算法分析2中的p数组

/*
	二分 在离散后的p数组中 查找v在p中的下标 
*/
int search(int x,int y,ll v){
	int m;
	while(x<y){
		m=x+(y-x)/2;
		if(p[m]==v) return m;
		else if(p[m]>v) y=m;
		else x=m+1;
	}
	return -1;
}

int main(){
	cin>>n;
	for(int i=0;i<n;i++){//输入n条着火信息 
		cin>>l[i];//一条着火位置起点
		cin>>r[i];//一条着火位置终点
		p[len++]=l[i];//起点存入p数组 
		p[len++]=r[i];//终点存入p数组 
	}
	sort(p,p+len);//p数组排序 
	
	for(int i=0;i<n;i++){//离散化后的坐标 
		l[i]=search(0,len,l[i]);//p数组中找l[i]对应的位置放入l[i]
		r[i]=search(0,len,r[i]);//p数组中找r[i]对应的位置放入r[i]
		for(int j=l[i];j<r[i];j++)//l[i]~r[i]所有在p中的下标 左闭右开赋值 
			vis[j]=true;//赋值vis[] true 
	}
	
//	for(int i=0;i<len;i++){//循环查看vis的连续true数 
//		if(vis[i]) ans+=p[i+1]-p[i];//由于左闭右开赋值 区间长度p[i+1]-p[i] 
//	}
	/*
		如果右连续true 累加到变量k 
		比如下标 1 2 3 对应2 5 8 
		1.其中1 true 2 true 3 false  2~5=3  5~8=3
		则k=3 则长度为:p[k]-[pos]=p[3]-p[1]= 8-2=6
		2.其中1 true 2 false 3 false  2~5=3 
		则k=2 则长度为:p[k]-[pos]=p[2]-p[1]= 5-2=6 
	*/ 
	int pos=0; 
	while(pos<len){ 
		if(vis[pos]==0) pos++;
		else{
			int k=pos;
			while(vis[k]) k++;//如果k为true k++ 线段长度记录在起点 
			ans+=p[k]-p[pos];
			pos=k;
		}
	}
	
	cout<<ans;
	return 0;
}

C++ STL 离散化

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=20050;
ll n,cLen=1,uLen,ans=0;
ll L[maxn],R[maxn];//存每艘船的左右边坐标,将其离散化之后再存回l r中  
ll c[maxn*2];
ll flag[maxn*2];//标记数组 

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>L[i];
        cin>>R[i];
        c[cLen++]=L[i];
        c[cLen++]=R[i];
    }
    sort(c+1,c+cLen+1);//排序 
    uLen = unique(c + 1, c + cLen+1) - c - 1;//去重 
    for(int i=1;i<=n;i++){
        L[i]=lower_bound(c+1,c+uLen+1,L[i])-c;//离散化下标 
        R[i]=lower_bound(c+1,c+uLen+1,R[i])-c-1;//离散化下标 左闭右开 所以 -1 
		for(ll j=L[i];j<=R[i];j++) flag[j]=1;//标记为true 
    }
    for(int i=1;i<=uLen;i++){//计算每一段标记长度 
        if(flag[i])  ans+=(c[i+1]-c[i]);
    }
    cout<<ans;
}

数组存储结构体线段

/*
离散化 就是把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小
https://www.jianshu.com/p/9347659dcf18 
*/
#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;

struct arr{
    int x, y;//起点 终点 
}a[maxn];//结构体存储 离散化 不存储无用的连续其他点 

int cmp(arr p, arr q){//按起点排序 升序 
    return p.x < q.x;
}
int ans,n;//ans 累加燃烧的长度和 n起火长度个数 
int main(){
    scanf("%d",&n);//输入起火的长度个数 
    for (int i=1;i<=n;i++)
        scanf("%d%d",&a[i].x,&a[i].y);//输入每条线段的起点 终点 
    sort(a+1,a+n+1,cmp);//按起点升序排序 
    int u = a[1].x, v = a[1].y;//u v记录当前前端的起点和终点 
    for (int i = 2; i <= n; i++){
        if (a[i].x > v){//如果当前的起点>上一个终点 累加上一次 记录本次的u v 
            ans+= v - u;//累加上一次 到ans 
            u = a[i].x;
            v = a[i].y;//记录本次的u v 
        }
        else if (a[i].y > v) v = a[i].y;//如果x不大于前一次或者前几次 y>v u不变 合并v 线段合并计算  
    }
    ans+= v - u;//累加最后一次线段长度 
    printf("%d", ans);
}

Blocked Billboard B
https://www.luogu.com.cn/problem/P4122

#include <iostream>
#include <algorithm>
using namespace std;

struct Rectangle{//(x1, y1)为左下坐标,(x2, y2)为右上的坐标
    int x1, y1, x2, y2;
}A, B, C;

int Area(Rectangle t){//计算矩形面积 长*宽 
    return (t.x2 - t.x1)*(t.y2 - t.y1);
}

bool check(Rectangle a, Rectangle b){//判断矩形a是否在矩形b内
//	在b左下角右上方 并且在b右上角的左下方 则矩形a在矩形b内 
    return (a.x1 >= b.x1 && a.y1 >= b.y1 
         && a.x2 <= b.x2 && a.y2 <= b.y2);
};

int X[10], Y[10];

int main()
{
    cin >> A.x1 >> A.y1 >> A.x2 >> A.y2;
    cin >> B.x1 >> B.y1 >> B.x2 >> B.y2;
    cin >> C.x1 >> C.y1 >> C.x2 >> C.y2;
    //输入6个点的x坐标 
    X[1] = A.x1, X[2] = A.x2, X[3] = B.x1;
    X[4] = B.x2, X[5] = C.x1, X[6] = C.x2;
    //输入6个点的y坐标 
    Y[1] = A.y1, Y[2] = A.y2, Y[3] = B.y1;
    Y[4] = B.y2, Y[5] = C.y1, Y[6] = C.y2;
    
    sort(X + 1, X + 7);//对x进行排序 
    sort(Y + 1, Y + 7);//对y进行排序 
    
    int ans = 0;//存放c无法覆盖的a、b面积和 
    
    for(int i = 1; i < 6; i ++){//从小到大遍历x 
        for(int j = 1; j < 6; j ++){//从小到大遍历y
			//构造x[i],y[i] -- x[i+1],y[i+1] 最小组成的矩形,可以覆盖a,b,c所有可能的情况 
            struct Rectangle R = {X[i], Y[j], X[i + 1], Y[j + 1]};
//          判断矩形R在A内 R在B内 且R不在C内的情况 
            if((check(R, A) || check(R, B)) && !check(R, C))
                ans += Area(R);//面积累加到ans 
        }
    }
    
    cout << ans << endl;
    return 0;
}

Tallest Cow S
https://www.luogu.com.cn/problem/P2879
程序自动分析
https://www.luogu.com.cn/problem/P1955
过河
https://www.luogu.com.cn/problem/P1052
火柴排队
https://www.luogu.com.cn/problem/P1966

posted @ 2022-04-30 14:13  new-code  阅读(45)  评论(0编辑  收藏  举报