[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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章