LeetCode 1035中等动态规划
不相交的线 图解题解
这道题到底在问什么
在两条水平线上分别按顺序写下 nums1 和 nums2。只有当 nums1[i] 等于 nums2[j] 时才能连一条线,且任意两条线不能相交(端点也不行,每个数最多属于一条线)。返回能连出的最大线数。
- 输入
- nums1=[1,4,2], nums2=[1,2,4]
- 输出
- 2 (连 1-1 与 4-4,再连 2-2 就会和 4-4 交叉)
- 输入
- nums1=[1,2,4], nums2=[1,2,4]
- 输出
- 3 (三个数顺序完全一致,全连上)
最优解:一步一步想明白
- 3记住这套「相等取左上加一,不相等取上、左较大」,下面每填一格都在套它。
- 4先搭表。行头是 nums1=[1,4,2],列头是 nums2=[1,2,4],最上一行和最左一列代表「一边为空」,连不出线,全部填 0(蓝色)。接下来从左上往右下一格格填内部。
- 5填 [1,1] 这格:nums1 的 1 和 nums2 的 1 正好相等,可以新连一条线。看它的左上角那格(黄色),在它的基础上加一条。
- 6左上角是 0,加上新连的这一条 = 1。这格落子 1(绿色)。
- 7填 [1,2] 这格:nums1 的 1 和 nums2 的 2 不相等,连不了新线,只能从上面一格和左边一格(两个黄色)里挑大的继承过来。
- 8上面是 0,左边是 1,取较大 = 1。这格落子 1(绿色)。
- 9填 [1,3] 这格:nums1 的 1 和 nums2 的 4 不相等,连不了新线,只能从上面一格和左边一格(两个黄色)里挑大的继承过来。
- 10上面是 0,左边是 1,取较大 = 1。这格落子 1(绿色)。
- 11填 [2,1] 这格:nums1 的 4 和 nums2 的 1 不相等,连不了新线,只能从上面一格和左边一格(两个黄色)里挑大的继承过来。
- 12上面是 1,左边是 0,取较大 = 1。这格落子 1(绿色)。
- 13填 [2,2] 这格:nums1 的 4 和 nums2 的 2 不相等,连不了新线,只能从上面一格和左边一格(两个黄色)里挑大的继承过来。
- 14上面是 1,左边是 1,取较大 = 1。这格落子 1(绿色)。
- 15填 [2,3] 这格:nums1 的 4 和 nums2 的 4 正好相等,可以新连一条线。看它的左上角那格(黄色),在它的基础上加一条。
- 16左上角是 1,加上新连的这一条 = 2。这格落子 2(绿色)。
- 17填 [3,1] 这格:nums1 的 2 和 nums2 的 1 不相等,连不了新线,只能从上面一格和左边一格(两个黄色)里挑大的继承过来。
- 18上面是 1,左边是 0,取较大 = 1。这格落子 1(绿色)。
- 19填 [3,2] 这格:nums1 的 2 和 nums2 的 2 正好相等,可以新连一条线。看它的左上角那格(黄色),在它的基础上加一条。
- 20左上角是 1,加上新连的这一条 = 2。这格落子 2(绿色)。
- 21填 [3,3] 这格:nums1 的 2 和 nums2 的 4 不相等,连不了新线,只能从上面一格和左边一格(两个黄色)里挑大的继承过来。
- 22上面是 2,左边是 2,取较大 = 2。这格落子 2(绿色)。
- 23表填满了。右下角 dp[3][3] = 2,就是最多能连出的线数。
- 24回看哪几步是「相等加一」:整张表其实有三格——[1,1] 连 1-1、[2,3] 连 4-4、[3,2] 连 2-2,都是踩着左上角加一长出来的。但 [2,3](4-4) 和 [3,2](2-2) 这两条会交叉,只能二选一;这条最优连线选了 [1,1] 和 [2,3] 两格(橙色),第三格 [3,2](蓝色)连上就会和 4-4 撞,所以最多仍是 2 条。
⚠️ 容易写错的地方
✗ 错:把它当成「最长公共子串」
✓ 对:是子序列,可以跳着选
连线只要求保持顺序,不要求挨着,所以是子序列不是子串
✗ 错:相等时去比上、左
✓ 对:相等时直接取左上角加一
相等取左上加一一定不劣,比上、左更准,写成 max 反而会少算
✗ 错:忘了留第 0 行第 0 列
✓ 对:表开成 (m+1)×(n+1),多出一圈 0
没有这圈 0,dp[1][1] 取左上角时会越界
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
m, n = len(nums1), len(nums2)
f = [[0] * (n + 1) for _ in range(m + 1)]
for i, x in enumerate(nums1, 1):
for j, y in enumerate(nums2, 1):
if x == y:
f[i][j] = f[i - 1][j - 1] + 1
else:
f[i][j] = max(f[i - 1][j], f[i][j - 1])
return f[m][n]C++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
int f[m + 1][n + 1];
memset(f, 0, sizeof(f));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (nums1[i - 1] == nums2[j - 1]) {
f[i][j] = f[i - 1][j - 1] + 1;
} else {
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
}
}
return f[m][n];
}
};Java
import java.util.*;
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
int[][] f = new int[m + 1][n + 1];
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (nums1[i - 1] == nums2[j - 1]) {
f[i][j] = f[i - 1][j - 1] + 1;
} else {
f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]);
}
}
}
return f[m][n];
}
}复杂度
时间
O(m×n)
两层循环填满整张 dp 表,每格 O(1)
空间
O(m×n)
一张二维表;可滚动数组压成 O(min(m,n))
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 不相交的线 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题和「最长公共子序列 LC1143」是什么关系?+
本质是同一道题。连线要求两数相等且顺序不乱,正是公共子序列的定义,最大线数就是最长公共子序列的长度,转移方程一模一样。把握住这个转化,这类「不交叉连线」的变体都能套 LCS。
空间能不能优化?+
能。每一格只依赖上一行和本行左边,所以可以用一维滚动数组,配一个临时变量记住「左上角」的旧值,把空间从 O(m×n) 压到 O(min(m,n))。时间仍是 O(m×n)。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 不相交的线 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。