[ACM]【BFS/压缩图/vector】Atcoder 168 .(Single Dot)

.(Single Dot)

传送门
题意:给若干横着和竖着的线段(栅栏),求位于(0,0)的牛可以往外走的面积。
在这里插入图片描述

思路:

我是真的不会(。
看了大佬给的题解,原来是把图片压缩,用二维网格表示,每条线都紧挨着,然后暴力BFS。(思路就是这么简单)
重点在于如何实现把图片压缩。
为了方便理解,画了三个图:
图一是原图的亚子。线段有正有负,牛在原点处。
图一
然后,我们根据原图的线段的相对位置,建立一个新图。此时,每两条线段之间,如果是空的,即变为紧挨着。粉色区域为无限区域,如(0,0)代表的是(-inf,-inf)。粉色区域以外为越界,不可达。
如果牛处于封闭的地段,那么BFS将永远不会到达粉色区域。而一旦到达了粉色区域,就可以绕着粉色区域转圈,因此,只要能达到无限,那么(0,0)是一定会被vis过的,不论是哪里没有封闭住。
线段们如何被表示呢?首先,我们得把图片变成二维网格(通过sort和unique和数组记录与查找)(紫色数字)。这样一来,每一条线段都夹缝在两排格子之间。我们用格子标记线段:如:如果有一条线段连接[0][2]和[2][2],那么在格子[0][1]、[1][1]与[0][2]、[1][2]之间便有一条线段,那么,我们设up[0][1]=1,u[1][1]=1和down[0][2]=1、down[1][2]=1,表示他们之间的这条线。
图二
问题又来了:如果牛的位置位于线段们的左边(如图),那么(实操)找lower_bound的时候,会把起点放在蓝色区域。而这时如果正好蓝色区域是封闭的,那么答案纠错了。(正确应该是能遍历到粉色区域为inf)
怎么解决这个问题呢?很简单,别这样投射,把起点投射到蓝色区域左下角的区域就行了。同样也是从牛出发,不过意味着从牛的左下角方块开始出发而已。
这个问题在牛位于右边的时候不会出现,因为是lower_bound,牛的投射点会刚好在粉色区域之中。
图三

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=3006;//最多要开3000,因为n那里输入两个,m那里输入1个,共3个
const int inf=1e9+7; 
typedef long long ll;
int dir[][2]={1,0,-1,0,0,1,0,-1};
int a[maxn],b[maxn],c[maxn],D[maxn],e[maxn],f[maxn];//用于查询
int u[maxn][maxn],d[maxn][maxn],l[maxn][maxn],r[maxn][maxn],vis[maxn][maxn];//记录线段(方格边界)
vector<int>xx({-inf,inf}),yy({-inf,inf});//先塞入粉色区域
void bfs(int x,int y){
	vis[x][y]=1;
	queue<pair<int,int>>q;
	q.push({x,y});
	while(!q.empty()){
		x=q.front().first,y=q.front().second;
		q.pop();
		for(int i=0;i<4;i++){
			if(i==0&&r[x][y]) continue;
			if(i==1&&l[x][y]) continue;
			if(i==2&&u[x][y]) continue;
			if(i==3&&d[x][y]) continue;
			int nx=x+dir[i][0];
			int ny=y+dir[i][1];
			if(nx<0||ny<0) continue;//越界
			if(nx>=xx.size()-1||ny>=yy.size()-1) continue;//越界
			if(vis[nx][ny]) continue;
			vis[nx][ny]=1;
			q.push({nx,ny});
		}
	}
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
		scanf("%d",&b[i]);
		scanf("%d",&c[i]);
		xx.push_back(a[i]);
		xx.push_back(b[i]);
		yy.push_back(c[i]);
	}
	for(int i=0;i<m;i++){
		scanf("%d",&D[i]);
		scanf("%d",&e[i]);
		scanf("%d",&f[i]);
		xx.push_back(D[i]);
		yy.push_back(e[i]);
		yy.push_back(f[i]);
	}
	sort(xx.begin(),xx.end());//先按照从小到大sort
	sort(yy.begin(),yy.end());
	xx.resize(unique(xx.begin(),xx.end())-xx.begin());//然后把重复项去掉!(压缩)
	yy.resize(unique(yy.begin(),yy.end())-yy.begin());
	for(int i=0;i<n;i++){
		//接下来对于每条线段,先把线段查找出来,再用新的压缩座标标记。
		//新的压缩座标是vector中的下标。
		int aa=lower_bound(xx.begin(),xx.end(),a[i])-xx.begin();
		int bb=lower_bound(xx.begin(),xx.end(),b[i])-xx.begin();
		int cc=lower_bound(yy.begin(),yy.end(),c[i])-yy.begin();
		for(int x=aa;x<bb;x++){
			u[x][cc-1]=1;
			d[x][cc]=1;
		}
	}
	for(int i=0;i<m;i++){
		int dd=lower_bound(xx.begin(),xx.end(),D[i])-xx.begin();
		int ee=lower_bound(yy.begin(),yy.end(),e[i])-yy.begin();
		int ff=lower_bound(yy.begin(),yy.end(),f[i])-yy.begin();
		for(int y=ee;y<ff;y++){
			l[dd][y]=1;
			r[dd-1][y]=1;
		}
	}
	//找到牛位置
	int x=lower_bound(xx.begin(),xx.end(),0)-xx.begin();
	int y=lower_bound(yy.begin(),yy.end(),0)-yy.begin();
	//用左下角方格做起点
	bfs(x-1,y-1);
	if(vis[0][0]){
		printf("INF\n");return 0;
	}
	ll ans=0;
	for(int i=0;i<xx.size()-1;i++){
		for(int j=0;j<yy.size()-1;j++){
			if(vis[i][j]) ans+=(ll)(xx[i+1]-xx[i])*(yy[j+1]-yy[j]);
		}
	}
	printf("%lld\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章