题目:
你将会获得一系列视频片段,这些片段来自于一项持续时长为 time 秒的体育赛事。这些片段可能有所重叠,也可能长度不一。
使用数组 clips 描述所有的视频片段,其中 clips[i] = [starti, endi] 表示:某个视频片段开始于 starti 并于 endi 结束。
甚至可以对这些片段自由地再剪辑:
- 例如,片段
[0, 7]可以剪切成[0, 1] + [1, 3] + [3, 7]三部分。
我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段([0, time])。返回所需片段的最小数目,如果无法完成该任务,则返回 -1 。
算法:
方法一:排序+dfs
思想:先将clips按照左边界排序,每个clip可以选择选或者不选,如果最终能得到times,比较得到最少的clip总数
思路没错,但是会TLE。
import "sort"
import "math"
func videoStitching(clips [][]int, time int) int {
sort.Slice(clips, func(i, j int) bool {
return clips[i][0] < clips[j][0]
})
if clips[0][0] != 0 {
return -1
}
fmt.Println(clips)
// 右边界达到的最远处
ans := math.MaxInt32
var dfs func(index, left, right, count int)
dfs = func(index, left, right, count int) {
fmt.Println(index, left, right)
if valid(left, right, time) {
ans = min(ans, count)
return
}
if index >= len(clips) {
return
}
// 如果clip[i]可以选择,可以用,也可以不用
if clips[index][0] == 0 || clips[index][0] <= right{
// 不用
dfs(index + 1, left, right, count)
// 用
if clips[index][0] == 0 {
left = clips[index][0]
right = clips[index][1]
}
dfs(index + 1, left, max(right, clips[index][1]), count + 1)
}
}
dfs(0, -1, -1, 0)
if ans == math.MaxInt32 {
fmt.Println("gg")
return -1
}
return ans
}
func valid(start, end, time int) bool {
if start == 0 && start + time <= end {
return true
}
return false
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
方法二:动态规划
func videoStitching(clips [][]int, time int) int {
dp := make([]int, time + 1)
for i := range dp {
dp[i] = 101
}
dp[0] = 0
for i := 1; i <= time; i ++ {
for j := range clips {
// clips[j]这段视频能覆盖到时间time=i
if clips[j][0] <= i && i <= clips[j][1] {
// 取最小的clip数。考虑边界情况
// [0,2],[2,5], 在对[0,2]求dp[2]已经得到了dp[2]=1,
// 在对[2,5]求dp[2]时,仍然时dp[2]=min(1, 1 + 1)=1
dp[i] = min(dp[i], dp[clips[j][0]] + 1)
}
}
}
if dp[time] == 101 {
return -1
}
return dp[time]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
方法三:贪心
思路:
step1. 预处理数组,得到maxn=[]int,index表示以该index为left的视频片段,value表示最右端距离。如clips=[[0,3],[3,5]]得到maxn=[]int{3,0,0,5,0,0},
step2. 初始视频能连续达到的最远距离last=0,遍历maxn更新最远距离last=max(last,maxn[i]),如果遍历到i时,最远距离还是i,则意味着视频中断了,如clips=[[0,2],[3,5]]得到maxn=[]int{2,0,0,5,0,0}。当最远距离变化时,count+1,为了比较最远距离是否变化,记录上一次的最远距离last
func videoStitching(clips [][]int, time int) int {
maxn := make([]int, time)
for i := range clips {
left, right := clips[i][0], clips[i][1]
// 为啥不能maxn长度不是time +1, 此处不考虑left == time呢,
// 情况1:[0,2][2,2],left == time多余,不能计算片段
// 情况1:[0,1][2,2],left == time,是不连续的视频
if left < time {
maxn[left] = max(maxn[left], right)
}
}
fmt.Println(maxn)
preLast, last := 0, 0
count := 0
for i := range maxn {
// 能到更远的地方?更新最远距离
if maxn[i] > last {
last = maxn[i]
}
// 最远距离到此位置了?
if i == last && i != time{
return - 1
}
// 达到上次最远的距离了?贪!
// 贪就贪在这里,达到当前视频片段的最远距离之后,才使用下一个视频片段,count++
// 才更新上一个最远距离
if i == preLast {
count ++
preLast = last
}
}
return count
}
func max(a, b int) int {
if a > b {
return a
}
return b
}