# Leetcode 第206场周赛题解

# Problem A - 二进制矩阵中的特殊位置 (opens new window)

枚举,时间复杂度O(nm(n+m))O(nm(n+m))。更好的做法是先记录每一行的11的位置,如果这一行只有一个11,再去检查这个11是否也是其所在列唯一的11,这样可以把时间复杂度优化到O(nm)O(nm),但因为比赛时数据范围非常小,直接暴力做法就可以了。

参考代码(C++)
class Solution {
public:
    int numSpecial(vector<vector<int>>& mat) {
        int ans = 0;
        int n = mat.size(), m = mat[0].size();
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                if (!mat[i][j])
                    continue;
                bool ok = true;
                for (int k = 0; k < n; ++k)
                    if (k != i && mat[k][j]) {
                        ok = false;
                        break;
                    }
                for (int k = 0; k < m; ++k)
                    if (k != j && mat[i][k]) {
                        ok = false;
                        break;
                    }
                ans += ok;
            }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# Problem B - 统计不开心的朋友 (opens new window)

首先把preferencespreferences转化为邻接矩阵的形式,然后遵循题意暴力枚举所有的朋友对,逐个检查是否不开心即可。

时间复杂度O(n2)O(n^2)

参考代码(C++)
class Solution {
public:
    int unhappyFriends(int n, vector<vector<int>>& preferences, vector<vector<int>>& pairs) {
        vector<vector<int>> mat(n, vector<int>(n));
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n - 1; ++j)
                mat[i][preferences[i][j]] = n - j;
        vector<bool> unhappy(n);
        for (auto p : pairs)
            for (auto q: pairs) {
                if (p == q)
                    continue;
                int x = p[0], y = p[1], u = q[0], v = q[1];
                if (mat[x][u] > mat[x][y] && mat[u][x] > mat[u][v])
                    unhappy[x] = true;
                if (mat[x][v] > mat[x][y] && mat[v][x] > mat[v][u])
                    unhappy[x] = true;
                if (mat[y][u] > mat[y][x] && mat[u][y] > mat[u][v])
                    unhappy[y] = true;
                if (mat[y][v] > mat[y][x] && mat[v][y] > mat[v][u])
                    unhappy[y] = true;
            }
        int ans = 0;
        for (int i : unhappy)
            ans += i;
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# Problem C - 连接所有点的最小费用 (opens new window)

最小生成树的模板题。因为任意两个点都可以连接,所以属于稠密图,有一种特殊的Prim算法可以达到O(V2)=O(n2)O(V^2)=O(n^2)的时间复杂度,而通常使用的Prim或Kruskal的时间复杂度为O(ElogV)=O(n2logn)O(E\log V)=O(n^2\log n)

常见的O(ElogV)O(E\log V)的最小生成树算法大家都比较熟悉了,这里着重讲一下O(V2)O(V^2)的特殊Prim算法。

这一算法的核心与我们熟悉的Prim算法是一致的,逐步加点。但这一算法中维护了一个全局的最小边集ee,其中eie_i代表从顶点viv_i出发的最短边的长度。显然,边集ee的大小为VV

每次我们从当前的最小边集中,找出未被选取过的点中的最小eie_i,然后将ii点加入点集,这一步操作的时间复杂度为O(V)O(V)

当我们选取一个新的点之后,就可以用这个新的点去更新所有未被选取的点所对应的eie_i。这一步操作的时间复杂度为O(V)O(V)

因为我们要添加VV个点,所以总时间复杂度为O(V2)O(V^2)

参考代码(C++)
#define INF 0x3f3f3f3f

class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = points.size();
        int ans = 0;
        vector<bool> vis(n);
        vector<int> min_dist(n, INF);
        min_dist[0] = 0;
        for (int i = 0; i < n; ++i) {
            int u = -1;
            int gmin = INF;
            for (int j = 0; j < n; ++j) {
                if (!vis[j] && min_dist[j] <= gmin) {
                    gmin = min_dist[j];
                    u = j;
                }
            }
            ans += gmin;
            vis[u] = true;
            for (int j = 0; j < n; ++j) 
                if (!vis[j]) {
                    int new_dist = abs(points[j][0] - points[u][0]) + abs(points[j][1] - points[u][1]);
                    min_dist[j] = min(min_dist[j], new_dist);
                }
        }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Problem D - 检查字符串是否可以通过排序子字符串得到另一个字符串 (opens new window)

观察到,操作前后,任何一种逆序对(所有满足i>ji>j的不同的二元组(i,j)(i,j))的数目都只能减少而不能增加。反过来,只要逆序对的数目不增加,就一定会存在一种合法的操作将其变换得到。(这一步的严格证明暂时还没有想到。)

因此,在排除sstt不是互为排列这一特殊情况之后,只要检查每种逆序数的数目是否有增加即可。

具体的做法是,记录下ss中第iicc前面0099的数字分别有多少个,然后对应检查tt中的第iicc前面的数字个数,看看逆序数数目是否有增加即可。

总时间复杂度为O(CN)O(CN),其中C=10C=10

参考代码(C++)
class Solution {
public:
    bool isTransformable(string s, string t) {
        vector<int> cs(10), ct(10);
        for (char c : s)
            cs[c - '0']++;
        for (char c : t)
            ct[c - '0']++;
        for (int i = 0; i < 10; ++i)
            if (cs[i] != ct[i])
                return false;
        vector<vector<vector<int>>> rs(10);
        vector<int> fs(10);
        for (int i = 0; i < s.size(); ++i) {
            int c = s[i] - '0';
            rs[c].push_back(fs);
            fs[c]++;
        }
        vector<int> ft(10);
        for (int i = 0; i < t.size(); ++i) {
            int c = t[i] - '0';
            int idx = ft[c];
            for (int j = 0; j < c; ++j) {
                if (ft[j] < rs[c][idx][j])
                    return false;
            }
            ft[c]++;
        }
        return true;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31