1. 程式人生 > >[洛谷3457][POI2007]POW-The Flood

[洛谷3457][POI2007]POW-The Flood

現在 down this ini ask ger mos nfa mmu

洛谷題目鏈接:[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 提供的翻譯

題目描述

Byteburg, the capital of Byteotia, is a picturesque city situated in a valley in the midst of mountains. Unfortunately, recent heavy rainfall has caused a flood - all the Byteburg is now completely under water. Byteasar, the king of Byteotia, has summoned his most enlightened advisors, including you, to a council. After long deliberations the council agreed to bring a few pumps, set them up in the flooded area and drain Byteburg.

The king has asked you to determine the minimum number of pumps sufficing to drain the city.

You are provided with a map of the city and the valley it is situated in. The map is in the shape of a m×nm\times nm×n rectangle, divided into unitary squares. For each such square the map tells its height above sea level and alsowhether it is a part of Byteburg or not. The whole area depicted in the map is under water. Furthermore, it issurrounded by much higher mountains, making the outflow of water impossible. Obviously, there is no needto drain the area that does not belong to Byteburg.

Each pump can be placed in any unitary square depicted in the map. The pump will be drawing thewater until its square is completely drained. Of course, the communicating tubes principle makes its work, so draining one square results in lowering the water level or complete draining of those squares from which the water can flow down to the one with the pump. Water can flow only between squares with a common side (or, more exact, squares whose projections onto horizontal plane have a common side, since the squares may be at different level). Apart from that, the water obviously only flows down.

Task

Write a programme that:

  • reads description of the map from the standard input,

  • determines the minimum number of pumps needed to drain whole Byteburg,

  • writes out the outcome to the standard output.

給定一張地勢圖,所有的點都被水淹沒,現在有一些關鍵點,要求放最少的水泵使所有關鍵點的水都被抽幹

輸入輸出格式

輸入格式:

In the first line of the standard input there are two integers mmm and nnn , separated by a single space, 1≤n,m≤1 0001 \le n, m \le 1?0001≤n,m≤1 000 . The following mmm lines contain the description of the map. The (i+1)(i+1)(i+1) ‘th line describes the iii ‘th row of unitary squares in the map. It contains nnn integers xi,1,xi,2,...,xinx_{i,1}, x_{i,2}, ..., x_{i_n}xi,1?,xi,2?,...,xin?? , separated by single spaces, ?1 000≤xi,j≤1 000-1?000 \le x_{i,j} \le 1?000?1 000≤xi,j?≤1 000 , xi,j≠1000x_{i,j} \ne 1000xi,j?≠1000 . The number xi,jx_{i,j}xi,j? describes the jjj ‘th square of the iii ‘th line. The ground level in this square is ∣xi,j∣|x_{i,j}|∣xi,j?∣ above sea level. If xi,j>0x_{i,j} > 0xi,j?>0 , then the square is part of Byteburg, otherwise it is outside the city. Notice, that the area of Byteburg need not be connected. In fact the city may have several separate parts.

輸出格式:

Your programme should write out one integer to the standard output - the minimum number of pumpsneeded to drain Byteburg.

輸入輸出樣例

輸入樣例#1:

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

輸出樣例#1:

2

一句話題意: 一個\(n*m\)的矩陣,矩陣上的值代表著該位置上的海拔高度(如果是負數則說明不再城市中),可以認為一開始水在無限高的地方,你需要在一些地方放水泵使得所有城市的位置的水位等於海拔高度.其中水可以向上下左右四個方向海拔比它低的地方流.

題解: 一開始看題目看了好久沒看懂樣例,後來看了題解才知道是像物理中一個連通器一樣的東西.對於一個城市要使它的水位等於海拔高度,那麽就至少要有個水泵在這個高度,或者更低,並且要和這個城市是連通的,也就是來他們兩點的路徑中沒有一個位置可以阻擋這個水流.

那麽該如何求出這個最小個數呢?我們考慮海拔最低的城市,顯然這個高度是需要一個水泵的.

然後對於每一個位置都可以向上下左右四個方向擴展一格,那麽最終統一高度的格子會形成一個連通塊,那麽對於這些連通塊只要有一個水泵,那麽這個連通塊內的所有格子的水都可以被抽幹.

所以我們可以想到用一個並查集來維護每個格子間的連通關系,並且通過水流的流向來不斷將海拔較低的位置向海拔較高的位置更新,也就是說,海拔較低的位置有一個水泵,並且另一個海拔較高的位置與它相連通的話,海拔高的位置上的水也會被抽幹.所以在維護並查集的同時再維護一下每個集合內是否含有水泵的情況就可以了.

然後因為水是從高向低流的,所以肯定要先考慮海拔較低的位置,然後才能用海拔較低的位置更新海拔高的位置.所以我們先將所有的點按照高度排序,然後按順序按海拔從低到高維護連通關系,就可以了.

#include<bits/stdc++.h>
using namespace std;
const int N=1000+5;
const int inf=2147483647;

int n, m, cnt = 0, ans = 0, vis[N][N], fa[N*N], have[N*N], hi[N][N], mv[] = {1, 0, -1, 0, 1};
// have 維護的是一些點連通的並查集內是否有水泵
// vis 維護的是某個位置所對應的節點編號(排序前)
// fa 維護並查集的連通關系
// hi 維護某個位置的高度

struct pos{
    int x, y, h, c;
}a[N*N];

inline int gi(){
    int ans = 0, f = 1; char i = getchar();
    while(i>'9' || i<'0'){ if(i == '-') f = -1; i = getchar(); }
    while(i>='0' && i<='9') ans = ans*10+i-'0', i = getchar();
    return ans * f;
}

inline bool cmp(pos a, pos b){
    return a.h < b.h;
}

inline int find(int x){
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

inline void comb(int x, int y){
    int r1 = find(x), r2 = find(y);
    if(r1 == r2) return;
    fa[r2] = r1, have[r1] |= have[r2];// r1是海拔高的位置
    // 所以只要海拔低的地方有抽水機, 海拔高的地方就有
}

int main(){
    // freopen("pow.in", "r", stdin);
    // freopen("pow.out", "w", stdout);
    int x, y; n = gi(), m = gi();
    memset(hi, 127, sizeof(hi));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[++cnt].x = i, a[cnt].y = j, a[cnt].h = gi(), vis[i][j] = cnt;
            if(a[cnt].h <= 0) a[cnt].h = -a[cnt].h, a[cnt].c = 0;
            else a[cnt].c = 1;
            hi[i][j] = a[cnt].h;
        }
    }
    for(int i=1;i<=cnt;i++) fa[i] = i;
    sort(a+1, a+cnt+1, cmp);
    for(int i=1;i<=cnt;i++){    
        x = a[i].x, y = a[i].y;
        for(int j=0;j<4;j++){
            int nx = x+mv[j], ny = y+mv[j+1];
            if(a[i].h >= hi[nx][ny]) comb(vis[x][y], vis[nx][ny]);
        } // 排序後用vis[x][y]是避免排序後編號發生變化,就與之前所對應的編號相連通
        if(a[i].h != a[i+1].h){ // 將同一高度的一起處理
            int j = i;
            while(a[i].h == a[j].h){
                if(a[j].c && have[find(vis[a[j].x][a[j].y])] == 0){
                    have[find(vis[a[j].x][a[j].y])] = 1, ans++;
                }
                j--;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

這道題因為我沒有考慮排序後編號的改變導致我調了一個多小時

[洛谷3457][POI2007]POW-The Flood