杂谈:离散化

1)一维离散化

洛谷1496

首先先看数据,n<=20000,数据不多,但是范围大(-10^9<=Ai,Bi<=10^9),这时,

就可以用离散化了。这是来自这道题的一篇题解中的话,其实这也是很多离散化题的

特征。

这道题可以暴力模拟过,但不是我们做这道题的目的。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
long n,m=1,ans=0;//m记录坐标数
long c[40100]={0};
//因为c要把起点和终点存下来,所以开二倍
int a[20100],b[20100];//a存起点,b存终点
bool flag[40100];//判断是否有效,两段没有交集,他们中间的段就无效
//个人理解,能否用作起点 
inline long read()//快读 
{
	long x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*=f;
}
inline long find(long key)//找到原来位置
{
	for(int i=1;i<=m;i++)
		if(c[i]==key) return i;
}
int main()
{
	n=read();
	for(long i=1;i<=n;i++)
	{
		a[i]=read();
		b[i]=read();
		c[m++]=a[i];
		c[m++]=b[i];
	}//把a,b存入c数组
	sort(c+1,c+m+1);
	for(long i=1;i<=n;i++)
	{
		a[i]=find(a[i]);
		b[i]=find(b[i]);//b[i]是终点的位置 
		for(long j=a[i];j<b[i];j++)	flag[j]=true;
	}
	for(long i=1;i<=m;i++)
	{
		if(flag[i]) ans+=c[i+1]-c[i];//终点减去起点 
	}
	printf("%ld\n",ans);
	return 0;
}
/*此题的离散化:
主要体现在flag数组的运用上,
没有具体的判断每个点,只是相对关系,
在flag数组的中,没有按输入一一匹配,
只要你可以当做起点,就可以用后面一个点去减掉 
*/

2)二维离散化(坐标离散化)

题目来自挑战P164例题

w*h的格子上花了n条或垂直或水平的宽度为1的直线。求这些线将格子划分成了多少个区域。

限制条件:1<=w,h<=1000000;1<=n<=500.

限制条件一看,离散化!

将前后没有变化的行列消除后并不会影响区域的个数,数组里只需要存有直线的行列以及其前

后的行列就足够了,这样的话数组开6n*6n即可,之后利用搜索求出区域的个数。

嗯,大概就是这样。我的理解也不是很深刻,下面的代码是书上的,我只能去尽力解释清楚代码

做了什么,至于为什么要这么做,证明什么的就免了,我也只是初学者(心虚),以后会了再回

来补坑。

代码:

#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=501;
int W,H,N;
int x1[maxn],x2[maxn],y1[maxn],y2[maxn];

bool fld[maxn*6][maxn*6];

//对x1和x2进行坐标离散化,并返回离散化之后的宽度 
int compress (int *x1,int *x2,int w)
{
	vector<int> xs;
	
	for(int i=0;i<N;i++)
	{
		for(int d=-1;d<=1;d++)//其前后的行列 
		{
			int tx1=x1[i]+d,tx2=x2[i]+d;
			if(1<=tx1&&tx1<=w) xs.push_back(tx1);
			if(1<=tx2&&tx2<=w) xs.push_back(tx2);
		}
	}
	sort(xs.begin(),xs.end());
	xs.erase(unique(xs.begin(),xs.end()),xs.end());//看不懂,去重用的 
	//unique()是C++标准库函数里面的函数,其功能是去除相邻的
	//重复元素(只保留一个),所以使用前需要对数组进行排序
	//返回去重后(不重复数列中)最后一个元素的尾地址
	//它并没有将重复的元素删除,而是把重复的元素放到数组的
	//最后面藏起来了,所以就很好解释erase了 
	for(int i=0;i<N;i++)
	{
		x1[i]=find(xs.begin(),xs.end(),x1[i])-xs.begin();//x1[i]在图中的相对位置 
		x2[i]=find(xs.begin(),xs.end(),x2[i])-xs.begin();//以元素个数为单位长度 
	}
	return xs.size();
}

void solve()
{
	//坐标离散化
	W=compress(x1,x2,W);
	H=compress(y1,y2,H);
	
	//填充有直线的部分
	memset(fld,0,sizeof(fld));
	for(int i=0;i<N;i++)
		for(int y=y1[i];y<y2[i];y++)
			for(int x=x1[i];x<x2[i];x++)
				fld[y][x]=true; //fld[y][x]我也不太懂是为什么  
	int ans=0;
	for(int y=0;y<H;y++)
	{
		for(int x=0;x<W;x++)
		{
			if(fld[y][x]) continue;
			ans++;//否则就是新区域,加一 
			
			//宽度优先搜索
			queue< pair<int,int> > que;
			que.push(make_pair(x,y));
			while(!que.empty())
			{
				int sx=que.front().first,sy=que.front().second;
				que.pop();
				
				for(int i=-1;i<=1;i++)
				{
					for(int j=-1;j<=1;j++)
					{
						if(i==j||i+j==0) continue;
						int tx=sx+i,ty=sy+j;
						if(tx<0||W<=tx||ty<0||H<=ty) continue;
						if(fld[ty][tx]) continue;
						que.push(make_pair(tx,ty));
						fld[ty][tx]=true;
					}
				}
			}
		}
	}
	
	printf("%d\n",ans);
}
int main()
{
	scanf("%d%d%d",&W,&H,&N);
	for(int i=0;i<N;i++)
	{
		scanf("%d%d%d%d",&x1[i],&x2[i],&y1[i],&y2[i]);
	}
	solve();
}
//代码还未调试成功,但大概思路代码已经展示出来

离散化,是一种很好的思想,在有些题中,只需要判定相对关系就可以得到答案,而且它的范围很大(1e9之类的),

而只给出很少的操作数(500之类的),此时就可以用离散化的思想解题。

PS:作者第一次相对比较系统地学习离散化,错误之处请指出,方便大家共同进步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值