题目:
不同金额的货币,可以使用多张,凑成目标值的方法数
分析:
这里我写了两种,
第一种不能重复选,类似背包问题,选择当前金额和不选择当前金额分别传index+1和rest递归,返回值相加
第二种可以重复选,选择当前金额n张,不能超过rest,然后递归,将返回值累加起来
代码:
package main
import(
"fmt"
)
//代表不同面值的数组,不重复选,凑成target的方法有几种,没有重复不用记忆化搜索
func process(list []int,index ,rest int) int{
if rest==0{
return 1
}
if index==len(list) && rest!=0{
return 0
}
return process(list,index+1,rest-list[index])+process(list,index+1,rest)
}
//动态规划版
//当rest=0时的值全为1,即第一列dp[i][0]=1,
//list的index为n的时候,第一列为1,其余全为0,因为初始化为0,上面一行已经处理了第一列为1,所以不再处理
//每一个格子的值来自于i+1行的j列或dp[i][j]+dp[i][j-list[i]],同背包问题,因此要从左下倒叙填,返回右上的值
func process1(list []int,tar int) int{
dp:=[][]int{}
for i:=0;i<=len(list);i++{
tmp:=[]int{}
for j:=0;j<=tar;j++{
tmp=append(tmp,0)
}
dp=append(dp,tmp)
}
for i :=0;i<=len(list);i++{
dp[i][0]=1
}
for i:=len(list)-1;i>=0;i--{
for j:=1;j<=tar;j++{
if j-list[i]>=0{
dp[i][j]=dp[i+1][j-list[i]]+dp[i+1][j]
}else{//注意!!!!不满足条件的等于下面的格子
dp[i][j]=dp[i+1][j]
}
}
}
return dp[0][tar]
}
//不同面值的人民币,可以重复选n张,凑够target,有重复计算,可以应用记忆化搜索加快计算
func process2(list []int,index ,rest int,dp [][]int) int{
if dp[index][rest]!=-1{
return dp[index][rest]
}
if rest==0{
dp[index][rest]=1
return dp[index][rest]
}
if index==len(list) {
if rest!=0{
dp[index][rest]=0
return dp[index][rest]
}else{
dp[index][rest]=1
return dp[index][rest]
}
}
result:=0
for num:=0;num*list[index]<=rest;num++{
result+=process2(list,index+1,rest-num*list[index],dp)
}
dp[index][rest]=result
return dp[index][rest]
}
//动态规划版
func process3(list []int,tar int)int{
dp:=[][]int{}
for i:=0;i<=len(list);i++{
tmp:=[]int{}
for j:=0;j<=tar;j++{
tmp=append(tmp,0)
}
dp=append(dp,tmp)
}
for i :=0;i<=len(list);i++{
dp[i][0]=1
}
//当前index依赖i+1,所以从左下开始倒叙填,行list元素,列rest
//第一列dp[i][0]=1
//每一个格子依赖i+1行的(rest-num*当前面值)格子的值
for i:=len(list)-1;i>=0;i--{
for j:=1;j<=tar;j++{
result:=0
// fmt.Printf("hreeeeeeeeeeeeeeeeeeee %v %v \n",i,j)
if j>=list[i]{//先知当前rest大于等于当前面值才累加result
for num:=0;num*list[i]<=tar;num++{
// fmt.Printf("--------------------- %v \n",j-num*list[i])
if j-num*list[i]<0{//不越界
break
}
result+=dp[i+1][j-num*list[i]]
}
}
dp[i][j]=result//满足条件的值就是累加后的result,不满足条件就是0
}
}
// for j:=0;j<=len(list);j++{
// fmt.Println(dp[j])
// }
return dp[0][tar]
}
func main(){
list:=[]int{70,100,1,4,9,21,33,2,50}
tar:=105
fmt.Println(process(list,0,tar))
dp:=[][]int{}
for i:=0;i<len(list);i++{
tmp:=[]int{}
for j:=0;j<=tar;j++{
tmp=append(tmp,-1)
}
dp=append(dp,tmp)
}
process(list,0,tar)
fmt.Println(process1(list,tar))
list1:=[]int{5,10,20,50,100}
fmt.Println(process2(list1,0,tar,dp))
fmt.Println(process3(list1,tar))
}
总结:
1 由暴力递归到动态规划,是一个空间换时间的过程
2 递归的coding过程,在于不断的尝试