HDU 5925 Coconuts

原题链接:https://acm.hdu.edu.cn/showproblem.php?pid=5925

题意:

有一个横纵坐标范围是[1,1e9]的网格图,但是图上只有k个点(k<=200),求没有被点覆盖的网格能组成几个连通块,输出联通块的个数,并且按照连通块的个数从小到大的顺序输出每个连通块的个数

思路:

需要二维离散化求新图,再根据新图求老图的块数

二维离散化

对于n*m的图:

先将所有横坐标加入x数组,排序去重
然后再扫一遍x数组,与一维离散化不同的是,如果当前坐标不是前一个坐标+1(即两个坐标不相邻),那么当前坐标离散化的数值就得是前一个离散化之后的数值+1,即两个坐标在新图中相隔了一列

比如(3,300) (200,800) (201,600)这三个点
x:3 4 200
3->1,200和3不相邻,所以200->3,201和200相邻,201->4
y:300 600 800
300->1,600->3,800->5
所以上面三个点离散化之后是:(1,1),(3,5),(4,3)
如果想看新图中每个块在老图中占多少个块,那么我们就用nx[i]来记录新图中横坐标为i的点在老图中占几行,ny[i]来记录新图中纵坐标为i的点在老图中占几列,那么对于新图的一个点(i,j),他在老图中占的块数就是nx[i]*ny[j]

详细步骤

对于横坐标:
横坐标用x数组来进行离散化
将旧图映射为下标都从0开始的新图
先将0和n加入x数组
再将所有的点的横坐标都加入x数组
将x数组排序去重
nx数组来存新图每个点对应的老图的间隔(算联通块用)
mpx数组来存旧图中每个点对应的新图的坐标
从第二个元素,即i=1来扫描x数组:
当x[i]!=x[i-1]+1时,说明i和i-1在新图中相隔了一行,并且这一行在老图中的间隔是x[i]-x[i-1]-1,那么nx中就加一个元素x[i]-x[i-1],然后再加上x[i]占的间隔1,那么mpx[x[i]]就是当前nx数组的大小-1(因为映射从下标为0开始)

建成新图之后对于每个没有走过的点dfs一遍求块数就好了

#include<bits/stdc++.h>
#include<vector>
#include<map>
#define int long long
using namespace std;
const int N=500+10;
int n,m;
int k;
struct name{
	int x,y;
}q[N];
vector<int> x,y;
vector<int> nx,ny,ans;
map<int,int>mpx,mpy;
bool vis[N][N];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
//dfs遍历每个连通块求在老图中的大小
void dfs(int i,int j,int &con){
	vis[i][j]=true;
	con+=nx[i]*ny[j];
	for(int kk=0;kk<4;kk++){
		int xx=i+dx[kk];
		int yy=j+dy[kk];
		if(xx<0||xx>=nx.size() ||yy<0||yy>=ny.size() )continue;
		if(vis[xx][yy])continue;
		dfs(xx,yy,con);
	}
}
void sove(){
	cin>>n>>m;
	cin>>k;
	memset(vis,false,sizeof vis);
	x.clear();
	y.clear() ;
	mpx.clear();
	mpy.clear();
	ans.clear() ; 
	nx.clear() ;
	ny.clear() ;
	//先往x和y中分别加入(0,n)和(0,m)
	x.push_back(0);
	x.push_back(n);
	y.push_back(0);
	y.push_back(m);   
	//将所有点的横纵坐标都加入相应的x和y数组中 
	for(int i=1;i<=k;i++){
		cin>>q[i].x >>q[i].y ;
		x.push_back(q[i].x);
		y.push_back(q[i].y );  
	}
	//离散化x和y
	sort(x.begin() ,x.end() );
	sort(y.begin() ,y.end() );
	x.erase(unique(x.begin() ,x.end() ),x.end() );
	y.erase(unique(y.begin() ,y.end() ),y.end() );
	//分别遍历x和y,算出nx和mpx
	int numx=x.size() ;
	int numy=y.size() ;
	for(int i=1;i<numx;i++){
		if(x[i]!=x[i-1]+1) nx.push_back(x[i]-x[i-1]-1);
		nx.push_back(1);
		mpx[x[i]]=nx.size() -1;
	}
	for(int i=1;i<numy;i++){
		if(y[i]!=y[i-1]+1) ny.push_back(y[i]-y[i-1]-1);
		ny.push_back(1);
		mpy[y[i]]=ny.size() -1;
	}	
	//将原图中被占用的点在新图中标记
	for(int i=1;i<=k;i++){
		int xx=q[i].x ;
		int yy=q[i].y ;
		vis[mpx[xx]][mpy[yy]]=true;
	}
	//在新图中求联通块的个数和每个联通块的大小,ans存每个联通块在老图中的格子个数
	for(int i=0;i<nx.size() ;i++){
		for(int j=0;j<ny.size() ;j++){
			int sum=0;
			if(!vis[i][j]){
				dfs(i,j,sum);
				if(sum>0){
					ans.push_back(sum); 
				}
			}
		}
	}
	sort(ans.begin() ,ans.end() );
	cout<<ans.size() <<endl;
	for(int i=0;i<ans.size() ;i++){
		if(i==0)cout<<ans[i];
		else cout<<" "<<ans[i];
	}
	cout<<endl;
}
signed main(){
	int tt;
	cin>>tt;
	for(int ca=1;ca<=tt;ca++){
		printf("Case #%lld:\n",ca);
		sove();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值