1. 程式人生 > 其它 >單週賽 254 題解

單週賽 254 題解

本場周賽,略有難度,知識點:字串,構造,貪心,廣度優先搜尋,二分答案

本場周賽,略有難度,知識點:字串,構造,貪心,廣度優先搜尋,二分答案

作為子字串出現在單詞中的字串數目

給定字典 \(w\),給定字串 \(s\),判斷 \(w\) 中有多少字串是 \(s\) 的子串

題解

直接模擬

// go
func numOfStrings(patterns []string, word string) int {
    ans := 0
    for _, v := range(patterns) {
        if strings.Contains(word, v) {
            ans++
        }
    }
    return ans
}

構造元素不等於兩相鄰元素平均值的陣列

給定一個數組 \(A\),保證元素互不相同,要求重排列,使得除去頭尾的其他每一個元素都不是左右兩個元素的平均值

資料規定

\(1\leq n\leq 10^5\)

題解

將陣列排序,用兩個指標從頭尾開始掃,先放頭再放尾,就可以保證每一個元素都小於/大於相鄰的左右兩個元素,時間複雜度 \(O(n\log n)\)

// cpp
class Solution {
public:
    vector<int> rearrangeArray(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<int> ans;
        int i = 0, j = n - 1;
        while (i <= j) {
            if (i <= j) ans.push_back(nums[i++]);
            if (i <= j) ans.push_back(nums[j--]);
        }
        return ans;
    }
};
// go
func rearrangeArray(nums []int) []int {
    n := len(nums)
    sort.Ints(nums)
    ans := []int{}
    for i, j, k := 0, n - 1, 0; i <= j; k++ {
        if i <= j {
            ans, i = append(ans, nums[i]), i + 1
        }
        if i <= j {
            ans, j = append(ans, nums[j]), j - 1
        }
    }
    return ans
}

陣列元素的最小非零乘積

給定正整數 \(p\),給定陣列 \(A\),包含 1, 2, .., 2^p - 1 中的所有正整數

現在可以選擇陣列中的任意兩個元素 \(a,\ b\),把其中一位不同的二進位制位互相替換

例如對於 1011, 0100,可以換成 1111, 0000

可以執行任意多次操作,要求計算操作後陣列乘積的最小值

資料規定

\(1\leq p\leq 60\)

題解

可以執行任意多次操作,就很有搞頭了

\(m = 2^p - 1\),選取 \(a,\ m - a\),一定可以保證他們的二進位制互補,互補的含義是每一位都不相同

例如 \(m = 2^4 - 1 = 15\),選取 \(a = (0111)_2 = 7,\ b = (1000)_2 = 8\)

我們執行一定次數的操作,一定可以使得 \(a,\ b\) 最終成為 \(1,\ m - 1\),例如在上述例子中為 \(1,\ 14\),這樣的乘積是最小的

考慮互補的對數,一共有 \(2^{p - 1} - 1\) 對,每一對的乘積為 \(2^p - 2\),再乘上不配對的 \(2^p - 1\),因此最終的答案為

\[(2^p - 1)\cdot (2^p - 2)^{2^{p - 1} - 1} \]

利用快速冪,時間複雜度為 \(O(p)\)

// go
const mod int = 1e9 + 7

func minNonZeroProduct(p int) int {
    return (1<<p - 1) % mod * qpow(1<<p-2, 1<<(p-1)-1, mod) % mod
}

func qpow(a int, b int, mod int) int {
    ans := 1
    for a %= mod; b > 0; b >>= 1 {
        if b&1 != 0 {
            ans = ans * a % mod
        }
        a = a * a % mod
    }
    return ans
}

你能穿過矩陣的最後一天

給定一個 \(row\times col\) 的二進位制矩陣,每一天都會有一個位置水漫金山,有水的位置用 \(1\) 表示,其他地方用 \(0\)

你可以從第一行的任意位置出發,從最後一行的任意一個位置離開,請計算出能夠安全離開矩陣的最後一天

題解

二分答案,然後用 bfs 判斷可行性

判定 \(k\) 是否可行,只要設定一個全 \(0\) 矩陣,將前 \(k\) 天的水漫金山情況用 \(1\) 表示,然後將第一行所有不為 \(0\) 的位置放入佇列進行 bfs 即可,設 \(M = col\cdot row\),時間複雜度 \(O(M \log M)\)

// cpp
#define pii pair<int, int>
const int DX[] = {1, 0, -1, 0};
const int DY[] = {0, -1, 0, 1};
bool bfs(int d, int m, int n, vector<vector<int>> &grid) {
    queue<pii> q;
    for (int i = 1; i <= n; ++i)
        if (!grid[1][i]) q.push(pii{1, i}), grid[1][i] = 1;
    
    while (!q.empty()) {
        auto [x, y] = q.front(); q.pop();
        if (x == m) return 1;
        for (int i = 0; i < 4; ++i) {
            int tx = DX[i] + x;
            int ty = DY[i] + y;
            if (tx >= 1 && tx <= m && ty >= 1 && ty <= n && !grid[tx][ty])
                q.push(pii{tx, ty}), grid[tx][ty] = 1;
        }
    }
    return 0;
}
class Solution {
public:
    int latestDayToCross(int row, int col, vector<vector<int>>& cells) {
        int m = cells.size(), n = cells[0].size();
        int l = 0, r = row * col, ans = 0;
        while (l <= r) {
            int mid = (l + r) >> 1;
            vector<vector<int>> grid(row + 7, vector<int>(col + 7, 0));
            for (int i = 0; i < mid; ++i) grid[cells[i][0]][cells[i][1]] = 1;
            if (bfs(mid, row, col, grid)) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        return ans;
    }
};