1. 程式人生 > 其它 >acwing-2060. 奶牛選美

acwing-2060. 奶牛選美

聽說最近兩斑點的奶牛最受歡迎,約翰立即購進了一批兩斑點牛。
不幸的是,時尚潮流往往變化很快,當前最受歡迎的牛變成了一斑點牛。
約翰希望通過給每頭奶牛塗色,使得它們身上的兩個斑點能夠合為一個斑點,讓它們能夠更加時尚。
牛皮可用一個 N×M 的字元矩陣來表示,如下所示:

................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

其中,X 表示斑點部分。
如果兩個 X 在垂直或水平方向上相鄰(對角相鄰不算在內),則它們屬於同一個斑點,由此看出上圖中恰好有兩個斑點。
約翰牛群裡所有的牛都有兩個斑點


約翰希望通過使用油漆給奶牛儘可能少的區域內塗色,將兩個斑點合為一個。
在上面的例子中,他只需要給三個 .. 區域內塗色即可(新塗色區域用 ∗ 表示):

................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....

請幫助約翰確定,為了使兩個斑點合為一個,他需要塗色區域的最少數量。

輸入格式

第一行包含兩個整數 N 和 M。

接下來 N 行,每行包含一個長度為 M 的由 X 和 . 構成的字串,用來表示描述牛皮圖案的字元矩陣。

輸出格式

輸出需要塗色區域的最少數量。

資料範圍

1≤N,M≤50

輸入樣例:

6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

輸出樣例:

3

方法一:遍歷兩個斑點的所有可能距離

先分別用兩個vector存兩個斑點上的所有點,遍歷點上的所有曼哈頓距離(|x1-x2|+|y1-y2|),取最小值

寫法一:

一開始還沒理清思路,寫下了這個樂色寫法:先BFS找到一個斑點上的其中一個點,再BFS從該點找到該斑點,再遍歷矩陣找到另一個斑點,最後遍歷計算曼哈頓距離

#include <bits/stdc++.h>

using namespace std;

struct Node {
    int x{}, y{};

    Node(int x, int y) {
        this->x = x;
        this->y = y;
    }

    Node() = default;;
};

string matrix[51];
bool flag[51][51];
int n, m;

int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};

void pushAdj(queue<Node> &q, Node &node) {
    for (int i = 0; i < 4; i++) {
        int a = node.x + dx[i], b = node.y + dy[i];
        if (a >= 0 && a < m && b >= 0 && b < n && !flag[b][a]) {
            q.emplace(a, b);
            flag[b][a] = true;
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
        cin >> matrix[i];

    // 尋找其中一個斑點
    Node node;
    memset(flag, 0, sizeof(flag));
    flag[0][0] = true;
    queue<Node> q;
    q.push(Node(0, 0));
    while (!q.empty()) {
        node = q.front();
        q.pop();
        if (matrix[node.y][node.x] == 'X') break;
        pushAdj(q, node);
    }
    while (!q.empty()) q.pop();

    // 獲取兩個斑點
    vector<Node> dot1, dot2;
    memset(flag, 0, sizeof(flag));
    q.push(node);
    while (!q.empty()) {
        node = q.front();
        q.pop();
        if (matrix[node.y][node.x] == 'X') {
            matrix[node.y][node.x] = 'a';
            dot1.emplace_back(node.x, node.y);
            pushAdj(q, node);
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++)
            if (matrix[i][j] == 'X') dot2.emplace_back(j, i);
    }

    // 計算最短距離
    int res = INT_MAX;
    for (const auto &d1 : dot1) {
        for (const auto &d2 : dot2)
            res = min(res, abs(d1.x - d2.x) + abs(d1.y - d2.y));
    }

    printf("%d", res-1);

    return 0;
}

寫法二:

為了省去BFS帶來的麻煩,使用DFS,在visit結點時,如果是'X'就入隊。最終得到兩個連通分量points[0]和[1],遍歷計算曼哈頓距離

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> pii;
string matrix[51];
int n, m;
vector<pii> points[2];

int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};

void dfs(int x, int y, vector<pii> &point) {
    point.emplace_back(x, y);
    for (int i = 0; i < 4; i++) {
        int a = x + dx[i], b = y + dy[i];
        if (a >= 0 && a < n && b >= 0 && b < m && matrix[a][b] == 'X') {
            matrix[a][b] = '.';
            dfs(a, b, point);
        }
    }
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        cin >> matrix[i];

    int k = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++)
            if (matrix[i][j] == 'X')
                dfs(i, j, points[k++]);
    }

    int res = INT_MAX;
    for (const auto &d1 : points[0]) {
        for (const auto &d2 : points[1])
            res = min(res, abs(d1.first - d2.first) + abs(d1.second - d2.second));
    }
    cout << res - 1;
}

方法二:雙端佇列

TODO