来源 :挑战程序设计
题目:w*h的格子上画了n条垂直或水平的宽度为1的直线。求出这些线将格子划分成了多少个区域。
1<=w,h<=1000000. 1<=n<=500
思路:首先,一般会想到直接进行dfs或bfs,但w,h过大,无法直接搜索。坐标离散化的思想就是把有用的坐标提取出来,再建一个坐标系,把这些坐标按照顺序放在这个坐标系中。本题只需存储直线的x,y坐标和直线前后的。所以大小6n*6n就够了。
坐标离散化前后图
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#define ll __int64
#define INF 0x3fffffff
#define MAXN 505
using namespace std;
int w,h,n;
int dir[4][2]={{-1,0},{0,-1},{0,1},{1,0}};
int x1[MAXN],x2[MAXN],Y1[MAXN],y2[MAXN];
bool fld[MAXN*6][MAXN*6];//填充用
struct Point
{
int x,y;
Point(int x_=0,int y_=0):x(x_),y(y_){}
}point[MAXN*MAXN*6*6];
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(tx1>=1&&tx1<=w) xs.push_back(tx1);
if(tx2>=1&&tx2<=w) xs.push_back(tx2);
}
}
sort(xs.begin(),xs.end());//有重复的,排序
xs.erase(unique(xs.begin(),xs.end()),xs.end());
for(int i=0;i<n;i++){
x1[i]=find(xs.begin(),xs.end(),x1[i])-xs.begin();//离散化后的坐标
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,false,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[x][y]=true;
}
}
}
//求区域的个数
int ans=0;
for(int x=0;x<w;x++){
for(int y=0;y<h;y++){
if(fld[x][y]){
continue;
}
ans++;
queue<Point>q;
Point p;
p.x=x;p.y=y;
q.push(p);
while(!q.empty()){
int sx=q.front().x;
int sy=q.front().y;
q.pop();
for(int i=0;i<4;i++){
int xx=sx+dir[i][0];
int yy=sy+dir[i][1];
if(fld[xx][yy]||xx<0||xx>=w||yy<0||yy>=h) continue;
q.push(Point(xx,yy));
fld[xx][yy]=true;
}
}
}
}
cout<<ans<<endl;
}
int main()
{
cin>>w>>h>>n;
for(int i=0;i<n;i++){
cin>>x1[i];
}
for(int i=0;i<n;i++){
cin>>x2[i];
}
for(int i=0;i<n;i++){
cin>>Y1[i];
}
for(int i=0;i<n;i++){
cin>>y2[i];
}
solve();
return 0;
}
样例
输入
w=10 h=10 n=5
x1={1,1,4,9,10}
x2={6,10,4,9,10}
y1={4,8,1,1,6}
y2={4,8,10,5,10}
输出
6