一只青蛙想要過河。 假定河流被等分為若干個單元格,并且在每一個單元格內都有可能放有一塊石子(也有可能沒有)。 青蛙可以跳上石子,但是不可以跳入水中。
給你石子的位置列表 stones(用單元格序號 升序 表示), 請判定青蛙能否成功過河(即能否在最后一步跳至最后一塊石子上)。
開始時, 青蛙默認已站在第一塊石子上,并可以假定它第一步只能跳躍一個單位(即只能從單元格 1 跳至單元格 2 )。
如果青蛙上一步跳躍了 k 個單位,那么它接下來的跳躍距離只能選擇為 k - 1、k 或 k + 1 個單位。 另請注意,青蛙只能向前方(終點的方向)跳躍。
示例 1:
輸入:stones = [0,1,3,5,6,8,12,17]
輸出:true
解釋:青蛙可以成功過河,按照如下方案跳躍:跳 1 個單位到第 2 塊石子, 然后跳 2 個單位到第 3 塊石子, 接著 跳 2 個單位到第 4 塊石子, 然后跳 3 個單位到第 6 塊石子, 跳 4 個單位到第 7 塊石子, 最后,跳 5 個單位到第 8 個石子(即最后一塊石子)。
示例 2:
輸入:stones = [0,1,2,3,4,8,9,11]
輸出:false
解釋:這是因為第 5 和第 6 個石子之間的間距太大,沒有可選的方案供青蛙跳躍過去。
解題思路
數組含義
dp[i][k]為真時代表站在i位置的小青蛙,是從上一個位置(i-k)跳k步的到達的。這也代表了下次小青蛙可以跳k-1,k,k+1步3種選擇
轉態轉移
小青蛙在i位置時,遍歷前面的位置j,看一下前面是否有位置是可以跳過來的。
這需要看2個因素,一是兩個石頭的距離(gap:=stones[i]-stones[j]),二是位于前方石頭的小青蛙是否剛剛好可以選擇跳gap步到達當前i位置。
而可以選擇gap步的只有dp[j][gap],dp[j][gap-1],dp[j][gap+1]這3種狀態,因為如果dp[j][gap-1]為真,代表站在j位置的小青蛙下次可以跳gap,gap-2,gap-1 這3個選擇,而gap步數就是我們需要的。因此狀態轉移為 dp[i][gap]=dp[j][gap]||dp[j][gap-1]||dp[j][gap+1]
為什么步數最多只有n步
因為每一次小青蛙跳的步數,最多只能是上一步加一,而第一步最多只能跳1格,因此跳到n個石頭的步數最多也只是n,因此位于第j個石頭的小青蛙最多只能j+1步,例如第0個石頭的小青蛙只能跳1格,第1個石頭的小青蛙只能跳2格
因此如果gap>j+1,就說明了不可能從j位置跳到當前位置
代碼
func canCross(stones []int) bool {n:=len(stones)dp := make([][]bool, n)for i := range dp {dp[i]=make([]bool,n)}dp[0][0]=truefor i := 1; i < n; i++ {for j := i-1; j >=0 ; j-- {gap:=stones[i]-stones[j]if gap<=j+1{ dp[i][gap]=dp[j][gap]||dp[j][gap-1]||dp[j][gap+1]if i==n-1&&dp[i][gap]{return true}}}}return false
}