1024.视频拼接

120 阅读1分钟

题目:
你将会获得一系列视频片段,这些片段来自于一项持续时长为 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
}