在二維地圖上, 0
代表海洋, 1
代表陸地,我們最多隻能將一格 0
海洋變成 1
變成陸地。
進行填海之後,地圖上最大的島嶼面積是多少?(上、下、左、右四個方向相連的 1
可形成島嶼)
示例 1:
輸入: [[1, 0], [0, 1]]
輸出: 3
解釋: 將一格0變成1,最終連通兩個小島得到面積為 3 的島嶼。
示例 2:
輸入: [[1, 1], [1, 0]]
輸出: 4
解釋: 將一格0變成1,島嶼的面積擴大為 4。
示例 3:
輸入: [[1, 1], [1, 1]]
輸出: 4
解釋: 沒有0可以讓我們變成1,面積依然為 4。
說明:
1 <= grid.length = grid[0].length <= 50
0 <= grid[i][j] <= 1
思路:
通過題目我們尋找一種遍歷的方法,對每個數字0,判斷如果將其填海,面積為多少,記為area
。
用中文敘述就是:
area初始化為0
如果此海上面為陸地,area+上面連成片的陸地面積。
如果此海右面為陸地,area+右面連成片的陸地面積。
如果此海下面為陸地,area+下面連成片的陸地面積。
如果此海左面為陸地,area+左面連成片的陸地面積。
程式碼表示則是:
if(g[i-1][j]==1)area+=上面連成片的陸地面積;
if(g[i][j+1]==1)area+=右面連成片的陸地面積;
if(g[i+1][j]==1)area+=下面連成片的陸地面積;
if(g[i][j-1]==1)area+=左面連成片的陸地面積;
本題的目的就是找到這個最大的area
。
可是遍歷的時候會有兩個麻煩點
- 如果上下左右為連成一片的陸地呢,那按照上面的公式則會重複計算面積
- 邊界判斷
其實2好解決,每次查詢的時候進行邊界檢查即可。
但是1的解決方式就很複雜,我的解決方式是通過增加兩個記錄陣列,給連成片的陸地進行編號,計算上下左右的陸地面積,若遇到有相同的陸地編號則不進行計算。
unsigned tag[50][50]={0};
/*用來記錄第X,Y塊屬於哪塊陸地片,如果是0則表示這塊屬於海洋,初始化全部為0*/
int area[700]={0};
/***
每個塊號面積為多少,記作area[tag_number]=the_area_of_this tag
也可以用容器unordered_map<int,unsigned>area節省空間
***/
舉個例子,比如有一海洋為(X,Y),其中上面為陸地編號為1,右面為陸地(編號為1),下面的陸地編號也為1(說明這塊土地與(X,Y)上面那塊土地相連),左面為海(編號為0)
如下圖畫的,則area只用加一個標記為1的陸地面積其中一個就行了。
避開重複的數字有很多種方法,如unordered_map
,或者建一個大小為4的陣列,進行位運算這些都是一種方法,就不一一介紹了。
Solution
/*對程式碼解釋在程式碼的後面*/
class Solution
{
public:
unsigned tag[50][50] = {0};
int area[700] = {0};
unsigned landNum = 0, X, Y;
void search(const vector<vector<int>> &grid, const unsigned x, const unsigned y)
{
if (grid[x][y] == 1 && tag[x][y] == 0)
{
area[landNum]++;
tag[x][y] = landNum;
/*四個if是邊界檢查*/
if (x >= 1)
search(grid, x - 1, y);
if (x < X - 1)
search(grid, x + 1, y);
if (y >= 1)
search(grid, x, y - 1);
if (y < Y - 1)
search(grid, x, y + 1);
}
}
int largestIsland(vector<vector<int>> &grid)
{
unsigned maxAnswer = 0, ans;
X = grid.size();
Y = grid[0].size();
for (unsigned i = 0; i < X; i++)
{
for (unsigned j = 0; j < Y; j++)
{
if (grid[i][j] == 1 && tag[i][j] == 0)
{
landNum++;
search(grid, i, j);
}
}
}
/*define the direction*/
enum
{
UP = 0,
RIGHT,
DOWN,
LEFT
};
unsigned tagIsland[4];
unordered_map<unsigned,unsigned> count;
for (unsigned i = 0; i < X; i++)
{
for (unsigned j = 0; j < Y; j++)
{
if (grid[i][j] == 0)
{
ans = 1;
tagIsland[UP] = i >= 1 ? tag[i - 1][j] : 0;
tagIsland[RIGHT] = j < Y - 1 ? tag[i][j + 1] : 0;
tagIsland[DOWN] = i < X - 1 ? tag[i + 1][j] : 0;
tagIsland[LEFT] = j >= 1 ? tag[i][j - 1] : 0;
for (unsigned k = UP; k <= LEFT;k++){
if(count[tagIsland[k]]==0&&tagIsland[k]!=0){
ans += area[tagIsland[k]];
count[tagIsland[k]]=1;
}
}
maxAnswer = max(ans, maxAnswer);
count.clear();
}
}
}
if(maxAnswer==0)
return X * Y;
return maxAnswer;
}
};
我們首先通過search()函式對整個表進行回溯初始化,使所有單獨的陸地都被編號。
然後通過對每塊海洋填海後的總area進行判斷,保留最大面積
maxAnswer
,即每次計算出新的area後都有maxAnswer=area>maxAnswer?area:maxAnswer;
在最後return前,如果
maxAnswer=0
則表示全部都是陸地,可以用反證法驗證: 如果至少有一個海洋,maxAnswer
至少為1。全是陸地的話只用return整片陸地的總面積即可,即X*Y。
至此,這道題的其中一種做法就完成了。