动态规划之二维dp

二维dp简介

二维dp和普通dp本质一样,只是状态的维度变成二维,即,需要两个变量来定义子问题。
二维dp的更新可能会存在部分优化:前缀和、滚动数组等。

动态规划分析步骤(通用)

  • 拆分子问题
    将原问题拆解成子问题,找到问题之间的联系
  • 确定状态
    此处的状态代指不同的问题,例如dp[x]表示上x台阶的方案数,其中x就是状态;确定状态就是确定问题需要几个维度的已知变量。一般是“前n个xxx为m的最大价值/最小价值/方案数”等
  • 状态转移方程
    状态(子问题)之间如何转移,即一个状态由哪几个状态转移来,或者该状态可以转移到哪些状态
  • 实现
    按照循环、记忆化搜索等方式求解最终状态。

例题分析

1536数字三角形

题目链接:1536数字三角形
在这里插入图片描述

状态1:
<i,j>表示从(i,j)往下走的最大和

dpi,j = max(dpi+1,j,dpi+1,j+1) + ai.j

状态2:
<i,j>表示到达(i,j)的最大和

dpi,j = max(dpi-1,j-1,dpi-1,j) + ai.j

#解法一
N=int(input())# 三角形行数
a=[]
dp=[[0]*(N) for i in range(N)]
for i in range(N):
    a.append(list(map(int,input().split())))
 
# 状态转移方程
# dp[i][j]表示到达i,j的最大和
# dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j]
 
# i从上往下更新,枚举每一行
for i in range(N):
    #枚举第j列
    for j in range(i+1):
        #边界,指遍历到两个底角的情况
        if j==0:
            dp[i][j]=dp[i-1][j]+a[i][j]
        elif j==i:
            dp[i][j] = dp[i - 1][j-1] + a[i][j]
        #底边中间的数
        else:
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + a[i][j]
 
print(max(dp[N-1]))

#解法二
N=int(input())# 三角形行数
a=[]
dp=[[0]*(N) for i in range(N)]
for i in range(N):
    a.append(list(map(int,input().split())))
 
#状态转移方程:dp[i][j]= max(dp[i+1][j],dp[i+1][j+1]) +a[i][j]
 
# print(a)
# print(dp)
 
#i从下往上更新,枚举每一行
for i in range(N-1,-1,-1):
    for j in range(0,i+1):
        if i==N-1:
            dp[i][j]=a[i][j]
        else:
            dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + a[i][j]
 
        # print(dp[i][j])
 
print(dp[0][0])

389摆花

题目链接:389摆花
在这里插入图片描述

给定n种花,数量不超过ai,需要凑出m盆,求方案数
状态<i,j>:前i种花,数量为j盆的方案数:dp[n][m]
状态转移:dp[i][j] = dp[i-1][j] + dp[i-1][j-1] + … + dp[i-1][j-a[i]]
边界:dp[…][0] = 1

#n种花,m盆(位置)
n,m=map(int,input().split())
a=[0]+list(map(int,input().split()))
 
dp=[[0]*(m+1) for i in range(n+1)]
 
#边界
for i in range(n+1):
    dp[i][0]=1
 
for i in range(1,n+1):
    for j in range(1,m+1):
        for k in range(min(a[i],j)+1):
            dp[i][j]+=dp[i-1][j-k]
            dp[i][j]%1000007
 
print(dp[n][m])

3711选数异或

题目链接:3711选数异或
在这里插入图片描述

状态:dp[i][j]表示前i个数字异或得到j的方案数
状态转移方程:dp[i][j] = dp[i-1]j + dp[i-1]j^a[i]
边界:dp[0][0] = 1

n, x = map(int, input().split())
a = [0] + list(map(int, input().split()))
# dp[i][j] 表示前i个数字异或得到j的方案数
dp = [[0] * 64 for i in range(n + 1)]
dp[0][0] = 1
for i in range(1, n + 1):
    for j in range(64):
        dp[i][j] = (dp[i - 1][j] + dp[i - 1][j ^ a[i]]) % 998244353
print(dp[n][x])
 
# 以下是使用滚动数组的版本
n, x = map(int, input().split())
a = [0] + list(map(int, input().split()))
# dp[i%2][j] 表示前i个数字异或得到j的方案数(滚动数组)
dp = [[0] * 64 for i in range(2)]
dp[0][0] = 1
for i in range(1, n + 1):
    for j in range(64):
        dp[i % 2][j] = (dp[(i - 1) % 2][j] + dp[(i - 1) % 2][j ^ a[i]]) % 998244353
print(dp[n % 2][x])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐丶晚笙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值