算法42-先后抽牌输赢问题,从递归到动态规划

题目:

在这里插入图片描述

分析:

同一个轮次,,面对同样的L R,先手必然会选较大的值,后首面对的也是同样的L R,但因为是先手选完之后才选择,所以只能选其中较小的值

定义两个函数f()代表先手,它总是选最大值,
s()代表后手,他总是选最小值

在这里插入图片描述

动态规划一般原步骤:
1 确定被依赖的值,一般的第一列,第一行,最后一行等等这些值,用来作为依赖,后面的数据依赖它而产生,

分析递归代码,对于f,由LR,return list[i],可以先确定上图70-100-1-4这条lr斜线上的值,而对于s,这个值都是0,所以先填充这一列

2 确定填充的方向,顺序从[0][0]开始,或者倒序从[n][0]开始,对于本例,一般位置的格子的数据来自L+1,R-1,即左边和下方的位置,因此可以确定方向是左上到右下斜线的方向,上图红线

3,确定值的依赖,L+1,R-1

4确定但会数据的坐标,一般看主函数递归调用的传参,本例中为为[0][len(list)-1],即第一行最后一个位置

代码:

package main

package main

import(
	"fmt"
	"math"
)

func f(list []int,L,R int)int{
	if L==R{
		return list[L]
	}
	//选择拿左边/右边,加上我选择之后的后手拿牌的数,哪个大选哪个
	return int(math.Max(float64(list[L]+s(list,L+1,R)),float64(list[R]+s(list,L,R-1))))
}

func s(list []int,L,R int)int{
	if L==R{
		return 0//只剩一张牌,被先手拿走,剩余0
	}
	return int(math.Min(float64(f(list,L+1,R)),float64(f(list,L,R-1))))
}
//两个二维表,f先手,s后手,先手后手用两个表中的数据互相依赖填充
//表中的数据相当与递归函数返回的结果
//一般位置的格子的数据依赖左边和下边的格子,因此由左上斜向下填充
func process(list []int){
	//准备两个n*n的二位数组范别记录先手后手的数据
	f:=[][]int{}
	s:=[][]int{}

	for t:=0;t<len(list);t++{

		var tmp []int=make([]int,len(list))
		var tmp1 []int=make([]int,len(list))

		f=append(f,tmp)
		s=append(s,tmp1)
	}
	// fmt.Println(f)
	//我这里直接把L==R的数据填好,后面的数据依赖这组数据
	for i :=0;i<len(list);i++{
		for j :=0;j<len(list);j++{
			if i<=j{
				// fmt.Printf(" iiiii    jjjjjj   %v    %v   \n ",i,j)
				if i==j{
					f[i][j]=list[i]
					s[i][j]=0
				} 
			}
		}
	}
	//开始填入一般数据,在L<R的区域,沿着左上往右下斜线填入,因为数据要依赖L+1和R-1(正下和正左)
	i:=0
	j:=1
	start:=j//辅助换到下一斜线继续填数据
	for i <j && i<len(list) &&j<=len(list)-1 {
		// fmt.Printf(" iiiii  <<<<<<<<<<<<<<<<<<<<,  jjjjjj   %v    %v   \n ",i,j)

		f[i][j]=int(math.Max(float64(list[i]+s[i+1][j]),float64(list[j]+s[i][j-1])))
		// fmt.Println(f)

		s[i][j]=int(math.Min(float64(f[i+1][j]),float64(f[i][j-1])))
		// fmt.Println(s)
		i+=1
		j+=1
		//当列超出后,i回到第一行,j回到上一次初始j+1
		if j==len(list){
			j=start+(j-i)
			i=0
		}

	}
	//返回[0][len-1]处的值
	fmt.Println(f[0][len(list)-1])
	fmt.Println(s[0][len(list)-1])
	

}

func main(){
	// list:=[]int{53, 40, 6, 48, 1, 51, 35, 46, 16, 23, 32}
	list:=[]int{70,100,1,4}
	fmt.Println(int(math.Max(float64(f(list,0,len(list)-1)),float64(s(list,0,len(list)-1)))))
	process(list)
}

总结:

//两个二维表,f先手,s后手,先手后手用两个表中的数据互相依赖填充
//表中的数据相当与递归函数返回的结果
//一般位置的格子的数据依赖左边和下边的格子,因此由左上斜向下填充
//切片append操作会申请内存,效率不高,所以在new slice的时候最好固定cap

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值