Description

Given n points on a 1-D plane, where the ith point (from 0 to n-1) is at x = i, find the number of ways we can draw exactly k non-overlapping line segments such that each segment covers two or more points. The endpoints of each segment must have integral coordinates. The k line segments do not have to cover all n points, and they are allowed to share endpoints.

Return the number of ways we can draw k non-overlapping line segments. Since this number can be huge, return it modulo 109 + 7.

Example 1:

[leetcode] 1621. Number of Sets of K Non-Overlapping Line Segments_参考文献

Input: n = 4, k = 2
Output: 5
Explanation:
The two line segments are shown in red and blue.
The image above shows the 5 different ways {(0,2),(2,3)}, {(0,1),(1,3)}, {(0,1),(2,3)}, {(1,2),(2,3)}, {(0,1),(1,2)}.

Example 2:

Input: n = 3, k = 1
Output: 3
Explanation: The 3 ways are {(0,1)}, {(0,2)}, {(1,2)}.

Example 3:

Input: n = 30, k = 7
Output: 796297179
Explanation: The total number of possible ways to draw 7 line segments is 3796297200. Taking this number modulo 109 + 7 gives us 796297179.

Example 4:

Input: n = 5, k = 3
Output: 7

Example 5:

Input: n = 3, k = 2
Output: 1

Constraints:

  • 2 <= n <= 1000
  • 1 <= k <= n-1

分析

题目的意思是:这道题就是把n分成k个非重叠的片段,不要求所有的点都用于划分,即指把其中的一部分划分成K个非重叠的片段也是可以的,如例1。这道题需要用动态规划,我参考了一下别人的思路。
记 dp[i][j] 表示使用 0 … i 的点构造了 j 条线段的方案数。我们需要区分第j条线段的右端点是否就是 i,因此可以考虑把 dp[i][j]拆分成两个状态:

  • dp[i][j][0] 表示第 j 条线段的右端点不是 i,也就是说我们没有办法继续延长第 j 条线段;
  • dp[i][j][1] 表示第 j 条线段的右端点就是 i,也就是说我们可以选择是否继续延长第 j 条线段。
  • 首先考虑 dp[i][j][0],因为第 j 条线段的右端点不是 i,因此第 i 个点没有用上,那么 0 … i-1 的点构造了 j 条线段,即
dp[i][j][0]=dp[i−1][j][0]+dp[i−1][j][1]
  • 再考虑 dp[i][j][1],因为第 j 条线段的右端点就是 i,因此有两种情况:
  • 第 j 条线段长度为 1,那么 0 … i-1 的点构造了 j-1 条线段,即
dp[i][j][1]=dp[i−1][j−1][0]+dp[i−1][j−1][1]
  • 第 j 条线段长度大于 1,那么删去第 j 条线段 i-1 … i 的这一部分,0 … i-1 的点仍然构造了 j 条线段,并且点 i-1 是属于第 j 条线段的,即
dp[i][j][1]=dp[i−1][j][1]
  • 加上边界条件 dp[0][0][0] = 1,最终答案即为 dp[n-1][k][0] + dp[n−1][k][1]

代码

class Solution:
def numberOfSets(self, n: int, k: int) -> int:
dp=[[[0]*2 for i in range(k+1)] for i in range(n) ]
# print(dp)
dp[0][0][0]=1
MOD=10**9+7
for i in range(1,n):
for j in range(k+1):
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1]%MOD
dp[i][j][1]=dp[i-1][j][1]
if(j>0):
dp[i][j][1]+=dp[i-1][j-1][0]
dp[i][j][1]=dp[i][j][1]%MOD
dp[i][j][1]+=dp[i-1][j-1][1]
dp[i][j][1]=dp[i][j][1]%MOD

return (dp[n-1][k][0]+dp[n-1][k][1])%MOD

参考文献

​大小为 K 的不重叠线段的数目​