LG_3457_[POI2007]POW-The Flood
題目描述
Description 你手頭有一張該市的地圖。這張地圖是邊長為 m∗n 的矩形,被劃分為m∗n個1∗1的小正方形。對於每個小正方形,地圖上已經標註了它的海拔高度以及它是否是該市的一個組成部分。地圖上的所有部分都被水淹沒了。並且,由於這張地圖描繪的地面周圍都被高山所環繞,洪水不可能自動向外排出。顯然,我們沒有必要抽乾那些非該市的區域。
每個巨型抽水機可以被放在任何一個1∗1正方形上。這些巨型抽水機將持續地抽水直到這個正方形區域裡的水被徹底抽乾為止。當然,由連通器原理,所有能向這個格子溢水的格子要麼被抽乾,要麼水位被降低。每個格子能夠向相鄰的格子溢水,“相鄰的”是指(在同一高度水平面上的射影)有公共邊。
Input
第一行是兩個數m,n(1<=m,n<=1000).
以下 m 行,每行 n 個數,其絕對值表示相應格子的海拔高度;若該數為正,表示它是該市的一個區域;否則就不是。
請大家注意:所有格子的海拔高度其絕對值不超過 1000 ,且可以為零.
Output
只有一行,包含一個整數,表示至少需要放置的巨型抽水機數目。
感謝@FlashHu 提供的翻譯
樣例
INPUT
6 9
-2 -2 -1 -1 -2 -2 -2 -12 -3
-2 1 -1 2 -8 -12 2 -12 -12
-5 3 1 1 -12 4 -6 2 -2
-5 -2 -2 2 -12 -3 4 -3 -1
-5 -6 -2 2 -12 5 6 2 -1
-4 -8 -8 -10 -12 -8 -6 -6 -4
OUTPUT
2
HINT
SOLUTION
並查集維護連通塊。
其實對於題面的“連通器”原理,考場上並沒有理解,導致根本看不懂樣例。
其實這個模型相信大家一定見過,只是我在場上完全沒有想起來。
很明顯地,當右邊的水位因為某種原因下降時,左邊的會一同下降。
而且當i,j相鄰,\(h_i\leq h_j\)時,若i點水被抽盡,j點一定也被抽盡。
所以根據這些原理,設我們的抽水機的高度為\(h_i\),那麼只要相鄰的點滿足\(h_j\leq h_i\)即可把i,j合併。
顯然本題是要我們維護連通塊,考慮使用bfs或並查集實現。
我們這裡使用的是並查集,列舉點向四周擴散合併。
為了方便列舉相同高度的點,我們考慮把所有點按高度升序排序。
當我們的高度相同的點列舉完,要進行一次統一合併之後再統計答案。
栗子:
資料:
1 4
3 3 -2 1
在列舉高度為3的點(左)之前,現在已有的連通塊情況:
(3)(3)(2 1)
若我們在列舉完左邊的3之後直接統計的話會出現情況:
(3 3)(2 1)
答案憑空多了1
因為我們做的不是bfs所以並不能像bfs那樣擴散的十分徹底。這個操作就可以避免出現“連通不徹底”的情況。
本題的思維難度較高。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
using namespace std;
inline int read(){
int x=0,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-48;ch=getchar();}
return x*f;}
const int N=1010;
const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int n,m,sq[N][N],fa[N*N],cnt=0;
bool used[N*N];
struct NODE{int x,y,h;}q[N*N];
struct NODE2{int x,y;}nd[N*N];
inline int find (int x) {return (x==fa[x])?x:fa[x]=find(fa[x]);}
bool cmp(NODE a,NODE b){return a.h<b.h;}
int main(){
int i,j;
n=read();m=read();int ans=0;
for (i=1;i<=n;++i) for (j=1;j<=m;++j){
sq[i][j]=read();fa[++cnt]=cnt;
q[cnt].x=i;q[cnt].y=j;q[cnt].h=abs(sq[i][j]);}
sort(q+1,q+1+cnt,cmp);memset(used,0,sizeof(used));
for (i=1;i<=cnt;++i){
int x=q[i].x,y=q[i].y;
int frm=(x-1)*m+y;
for (j=0;j<4;++j){
int nx=x+dx[j],ny=y+dy[j];
// puts("******");
if ((nx<1)||(ny<1)||(nx>n)||(ny>m)) continue;
if (abs(sq[x][y])<abs(sq[nx][ny])) continue;
// printf("(%d,%d):%d,%d\n",x,y,nx,ny);
int now=(nx-1)*m+ny;
int f1=find(frm),f2=find(now);
if (f2==f1) continue;
fa[f2]=f1;used[f1]|=used[f2];
}
if (q[i].h!=q[i+1].h){
for (j=i;(q[i].h==q[j].h);--j){
int x=find((q[j].x-1)*m+q[j].y);
if (sq[q[j].x][q[j].y]<=0) continue;
if (!used[x]) {used[x]=1;ans++;}
}
}
}
printf("%d\n",ans);
return 0;
}