
题目描述
给你两组点,其中第一组中有 size1
个点,第二组中有 size2
个点,且 size1 >= size2
。
任意两点间的连接成本 cost
由大小为 size1 x size2
矩阵给出,其中 cost[i][j]
是第一组中的点 i
和第二组中的点 j
的连接成本。如果两个组中的每个点都与另一组中的一个或多个点连接,则称这两组点是连通的。换言之,第一组中的每个点必须至少与第二组中的一个点连接,且第二组中的每个点必须至少与第一组中的一个点连接。
返回连通两组点所需的最小成本。
示例 1:

输入:cost = [[15, 96], [36, 2]]
输出:17
解释:连通两组点的最佳方法是:
1--A
2--B
总成本为 17 。
示例 2:

输入:cost = [[1, 3, 5], [4, 1, 1], [1, 5, 3]]
输出:4
解释:连通两组点的最佳方法是:
1--A
2--B
2--C
3--A
最小成本为 4 。
请注意,虽然有多个点连接到第一组中的点 2 和第二组中的点 A ,但由于题目并不限制连接点的数目,所以只需要关心最低总成本。
示例 3:
输入:cost = [[2, 5, 1], [3, 4, 7], [8, 1, 2], [6, 2, 4], [3, 8, 8]]
输出:10
提示:
size1 == cost.length
size2 == cost[i].length
1 <= size1, size2 <= 12
size1 >= size2
0 <= cost[i][j] <= 100
解法
方法一:状态压缩 + 动态规划
我们记第一组的点数为 \(m\),第二组的点数为 \(n\)。
由于 \(1 \leq n \leq m \leq 12\),因此,我们可以用一个整数来表示第二组中点的状态,即二进制表示的一个长度为 \(n\) 的整数,其中第 \(k\) 位为 \(1\) 表示第二组中的第 \(k\) 个点与第一组中的点连通,为 \(0\) 表示不连通。
接下来,我们定义 \(f[i][j]\) 表示第一组中的前 \(i\) 个点已经全部连通,且第二组中的点的状态为 \(j\) 时的最小成本。初始时 \(f[0][0] = 0\),其余值均为正无穷大。答案即为 \(f[m][2^n - 1]\)。
考虑 \(f[i][j]\),其中 \(i \geq 1\)。我们可以枚举第二组中的每个点 \(k\),如果点 \(k\) 与第一组中的第 \(i\) 个点连通,那么我们可以分以下两种情况讨论:
- 如果点 \(k\) 只与第一组中的第 \(i\) 个点连通,那么 \(f[i][j]\) 可以从 \(f[i][j \oplus 2^k]\) 或者 \(f[i - 1][j \oplus 2^k]\) 转移而来,其中 \(\oplus\) 表示异或运算;
- 如果点 \(k\) 与第一组中的第 \(i\) 个点以及其他点都连通,那么 \(f[i][j]\) 可以从 \(f[i - 1][j]\) 转移而来。
在上述两种情况中,我们需要选择转移值最小的那个,即有:
\[
f[i][j] = \min_{k \in \{0, 1, \cdots, n - 1\}} \{f[i][j \oplus 2^k], f[i - 1][j \oplus 2^k], f[i - 1][j]\} + cost[i - 1][k]
\]
最后,我们返回 \(f[m][2^n - 1]\) 即可。
时间复杂度 \(O(m \times n \times 2^n)\),空间复杂度 \(O(m \times 2^n)\)。其中 \(m\) 和 \(n\) 分别是第一组和第二组中的点数。
我们注意到 \(f[i][j]\) 的转移只与 \(f[i - 1][\cdot]\) 以及 \(f[i][\cdot]\) 有关,因此我们可以使用滚动数组优化空间复杂度,将空间复杂度优化到 \(O(2^n)\)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | class Solution:
def connectTwoGroups(self, cost: List[List[int]]) -> int:
m, n = len(cost), len(cost[0])
f = [[inf] * (1 << n) for _ in range(m + 1)]
f[0][0] = 0
for i in range(1, m + 1):
for j in range(1 << n):
for k in range(n):
if (j >> k & 1) == 0:
continue
c = cost[i - 1][k]
x = min(f[i][j ^ (1 << k)], f[i - 1][j], f[i - 1][j ^ (1 << k)]) + c
f[i][j] = min(f[i][j], x)
return f[m][-1]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | class Solution {
public int connectTwoGroups(List<List<Integer>> cost) {
int m = cost.size(), n = cost.get(0).size();
final int inf = 1 << 30;
int[][] f = new int[m + 1][1 << n];
for (int[] g : f) {
Arrays.fill(g, inf);
}
f[0][0] = 0;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 1 << n; ++j) {
for (int k = 0; k < n; ++k) {
if ((j >> k & 1) == 1) {
int c = cost.get(i - 1).get(k);
f[i][j] = Math.min(f[i][j], f[i][j ^ (1 << k)] + c);
f[i][j] = Math.min(f[i][j], f[i - 1][j] + c);
f[i][j] = Math.min(f[i][j], f[i - 1][j ^ (1 << k)] + c);
}
}
}
}
return f[m][(1 << n) - 1];
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | class Solution {
public:
int connectTwoGroups(vector<vector<int>>& cost) {
int m = cost.size(), n = cost[0].size();
int f[m + 1][1 << n];
memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 1 << n; ++j) {
for (int k = 0; k < n; ++k) {
if (j >> k & 1) {
int c = cost[i - 1][k];
int x = min({f[i][j ^ (1 << k)], f[i - 1][j], f[i - 1][j ^ (1 << k)]}) + c;
f[i][j] = min(f[i][j], x);
}
}
}
}
return f[m][(1 << n) - 1];
}
};
|
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 | func connectTwoGroups(cost [][]int) int {
m, n := len(cost), len(cost[0])
const inf = 1 << 30
f := make([][]int, m+1)
for i := range f {
f[i] = make([]int, 1<<n)
for j := range f[i] {
f[i][j] = inf
}
}
f[0][0] = 0
for i := 1; i <= m; i++ {
for j := 0; j < 1<<n; j++ {
for k := 0; k < n; k++ {
c := cost[i-1][k]
if j>>k&1 == 1 {
f[i][j] = min(f[i][j], f[i][j^(1<<k)]+c)
f[i][j] = min(f[i][j], f[i-1][j]+c)
f[i][j] = min(f[i][j], f[i-1][j^(1<<k)]+c)
}
}
}
}
return f[m][(1<<n)-1]
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | function connectTwoGroups(cost: number[][]): number {
const m = cost.length;
const n = cost[0].length;
const inf = 1 << 30;
const f: number[][] = Array(m + 1)
.fill(0)
.map(() => Array(1 << n).fill(inf));
f[0][0] = 0;
for (let i = 1; i <= m; ++i) {
for (let j = 0; j < 1 << n; ++j) {
for (let k = 0; k < n; ++k) {
if (((j >> k) & 1) === 1) {
const c = cost[i - 1][k];
f[i][j] = Math.min(f[i][j], f[i][j ^ (1 << k)] + c);
f[i][j] = Math.min(f[i][j], f[i - 1][j] + c);
f[i][j] = Math.min(f[i][j], f[i - 1][j ^ (1 << k)] + c);
}
}
}
}
return f[m][(1 << n) - 1];
}
|
方法二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Solution:
def connectTwoGroups(self, cost: List[List[int]]) -> int:
m, n = len(cost), len(cost[0])
f = [inf] * (1 << n)
f[0] = 0
g = f[:]
for i in range(1, m + 1):
for j in range(1 << n):
g[j] = inf
for k in range(n):
if (j >> k & 1) == 0:
continue
c = cost[i - 1][k]
x = min(g[j ^ (1 << k)], f[j], f[j ^ (1 << k)]) + c
g[j] = min(g[j], x)
f = g[:]
return f[-1]
|
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 | class Solution {
public int connectTwoGroups(List<List<Integer>> cost) {
int m = cost.size(), n = cost.get(0).size();
final int inf = 1 << 30;
int[] f = new int[1 << n];
Arrays.fill(f, inf);
f[0] = 0;
int[] g = f.clone();
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 1 << n; ++j) {
g[j] = inf;
for (int k = 0; k < n; ++k) {
if ((j >> k & 1) == 1) {
int c = cost.get(i - 1).get(k);
g[j] = Math.min(g[j], g[j ^ (1 << k)] + c);
g[j] = Math.min(g[j], f[j] + c);
g[j] = Math.min(g[j], f[j ^ (1 << k)] + c);
}
}
}
System.arraycopy(g, 0, f, 0, 1 << n);
}
return f[(1 << n) - 1];
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | class Solution {
public:
int connectTwoGroups(vector<vector<int>>& cost) {
int m = cost.size(), n = cost[0].size();
const int inf = 1 << 30;
vector<int> f(1 << n, inf);
f[0] = 0;
vector<int> g = f;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 1 << n; ++j) {
g[j] = inf;
for (int k = 0; k < n; ++k) {
if (j >> k & 1) {
int c = cost[i - 1][k];
int x = min({g[j ^ (1 << k)], f[j], f[j ^ (1 << k)]}) + c;
g[j] = min(g[j], x);
}
}
}
f.swap(g);
}
return f[(1 << n) - 1];
}
};
|
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 | func connectTwoGroups(cost [][]int) int {
m, n := len(cost), len(cost[0])
const inf = 1 << 30
f := make([]int, 1<<n)
for i := range f {
f[i] = inf
}
f[0] = 0
g := make([]int, 1<<n)
for i := 1; i <= m; i++ {
for j := 0; j < 1<<n; j++ {
g[j] = inf
for k := 0; k < n; k++ {
c := cost[i-1][k]
if j>>k&1 == 1 {
g[j] = min(g[j], g[j^1<<k]+c)
g[j] = min(g[j], f[j]+c)
g[j] = min(g[j], f[j^1<<k]+c)
}
}
}
copy(f, g)
}
return f[1<<n-1]
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | function connectTwoGroups(cost: number[][]): number {
const m = cost.length;
const n = cost[0].length;
const inf = 1 << 30;
const f: number[] = new Array(1 << n).fill(inf);
f[0] = 0;
const g = new Array(1 << n).fill(0);
for (let i = 1; i <= m; ++i) {
for (let j = 0; j < 1 << n; ++j) {
g[j] = inf;
for (let k = 0; k < n; ++k) {
if (((j >> k) & 1) === 1) {
const c = cost[i - 1][k];
g[j] = Math.min(g[j], g[j ^ (1 << k)] + c);
g[j] = Math.min(g[j], f[j] + c);
g[j] = Math.min(g[j], f[j ^ (1 << k)] + c);
}
}
}
f.splice(0, f.length, ...g);
}
return f[(1 << n) - 1];
}
|