# Leetcode 第206场周赛题解
# Problem A - 二进制矩阵中的特殊位置 (opens new window)
枚举,时间复杂度。更好的做法是先记录每一行的的位置,如果这一行只有一个,再去检查这个是否也是其所在列唯一的,这样可以把时间复杂度优化到,但因为比赛时数据范围非常小,直接暴力做法就可以了。
参考代码(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
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)
首先把转化为邻接矩阵的形式,然后遵循题意暴力枚举所有的朋友对,逐个检查是否不开心即可。
时间复杂度。
参考代码(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
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算法可以达到的时间复杂度,而通常使用的Prim或Kruskal的时间复杂度为。
常见的的最小生成树算法大家都比较熟悉了,这里着重讲一下的特殊Prim算法。
这一算法的核心与我们熟悉的Prim算法是一致的,逐步加点。但这一算法中维护了一个全局的最小边集,其中代表从顶点出发的最短边的长度。显然,边集的大小为。
每次我们从当前的最小边集中,找出未被选取过的点中的最小,然后将点加入点集,这一步操作的时间复杂度为。
当我们选取一个新的点之后,就可以用这个新的点去更新所有未被选取的点所对应的。这一步操作的时间复杂度为。
因为我们要添加个点,所以总时间复杂度为。
参考代码(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
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)
观察到,操作前后,任何一种逆序对(所有满足的不同的二元组)的数目都只能减少而不能增加。反过来,只要逆序对的数目不增加,就一定会存在一种合法的操作将其变换得到。(这一步的严格证明暂时还没有想到。)
因此,在排除和不是互为排列这一特殊情况之后,只要检查每种逆序数的数目是否有增加即可。
具体的做法是,记录下中第个前面到的数字分别有多少个,然后对应检查中的第个前面的数字个数,看看逆序数数目是否有增加即可。
总时间复杂度为,其中。
参考代码(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
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