1)一维离散化
首先先看数据,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:作者第一次相对比较系统地学习离散化,错误之处请指出,方便大家共同进步