dp

本文介绍了动态规划的基本概念及其在典型问题中的应用,如数字金字塔、背包问题、最长上升子序列等,并提供了详细的代码示例。

dp啊..本蒟蒻从初二学到现在都没学好的东西啊QAQ
动态规划(dynamic programming)概念自行百度。【23333
持续更(lan)新(wei)中。
然后就先做这么点总结吧,留的坑再慢慢填上来emmm
逻辑很混乱…各位凑合着看吧。

由于本人语文实在太差,词不达意,这篇可能会讲得非常奇怪,请做好心理准备!


0.dp的一般思路

1.将原问题分解为子问题
2.确定状态
3.确定初始状态(边界状态)的值
4.确定状态转移方程

0.能用dp解决的问题的特点
1.问题具有最优子结构性质

如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质。

2.无后效性

当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。


1.数字金字塔

考虑在下面被显示的数字金字塔。 写一个程序来计算从最高点开始在底部任意处结束的路径经过数字的和的最大。每一步可以走到左下方的点也可以到达右下方的点。

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

在上面的样例中,从7 到 3 到 8 到 7 到 5 的路径产生了最大和:30。
数据小的时候,dfs当然是可以的。

#include<cstdio>
#include<algorithm>
#define maxn 1005
using namespace std;
int n,a[maxn][maxn];
int dfs(int i,int j)
{
    if(i==n) return a[i][j];
    return max(dfs(i+1,j),dfs(i+1,j+1))+a[i][j];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    printf("%d",dfs(1,1));
}

但是数据大起来就直接TLE了。因为有很多节点被重复遍历然后就GG了。
然后,记忆化搜索!这样搜索出来的节点就只会调用1次了吧,时间复杂度强行降低。
准确的说,从O(2n)O(2n)到了O(n2)O(n2)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1005
using namespace std;
int n,a[maxn][maxn],memo[maxn][maxn];
int dfs(int i,int j)
{
    if(memo[i][j]>=0) return memo[i][j];
    if(i==n) return memo[i][j]=a[i][j];
    return memo[i][j]=max(dfs(i+1,j),dfs(i+1,j+1))+a[i][j];
}
int main()
{
    scanf("%d",&n);
    memset(memo,-1,sizeof(memo));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    printf("%d",dfs(1,1));
}

最后就是递推了。预处理出最后一行,然后从下往上推。
//这个dp数组可以不要的说

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1005
using namespace std;
int n,a[maxn][maxn],dp[maxn][maxn];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++) dp[n][i]=a[n][i];
    for(int i=n-1;i>0;i--)
        for(int j=1;j<=i;j++)
            dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
    printf("%d",dp[1][1]);
}

2.背包

给定背包的容量m、物品个数n、物体的重量w[i]和价值v[i],求可以装下的最大的价值。
据我所知有01背包、完全背包和多重背包。
可以灵活变化。
时间:O(mn)O(mn)

2.1.01背包

顾名思义,就是一种东西只能装一个的背包问题。
考虑每个物品装还是不装,f[i][j]为前i个物品,容量为j时的最大价值,可以得到以下递推式:f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i])。
答案是f[n][m]。
如果开二维数组空间会炸怎么办?
显然(其实是我不能说的很清楚),可以看出这个状态只和前面的状态有关,可以把数组变成一维,即:

for(int i=1;i<=n;i++)
    for(int j=m;j>=w[i];j--)
        f[j]=max(f[j],f[j-w[i]]+v[i]);

第二层循环的顺序非常重要!
题目(未完待续):

2.2.完全背包

一种东西有无限个。
如果你打算在那两层循环中再枚举一次物品的个数就坐等TLE吧。
这里先给代码吧= =

for(int i=1;i<=n;i++)
    for(int j=w[i];j<=m;j++)
        f[j]=max(f[j],f[j-w[i]]+v[i]);

可以看出区别就在于第二层循环的顺序。
这很重要然而我就是说不清楚啊QAQ
题目(未完待续):

2.3.多重背包

一种东西不止一个也不一定是无限个。
还是那句话,如果你打算在那两层循环中再枚举一次物品的个数就坐等TLE吧。
一种比较靠谱的方法就是拆成1,2,4,…再用01背包解决。

int x,t=1;
while(t*2-1<x)
{
    a[++len]=t*i;
    t*=2;
}
a[++len]=(x-t+1)*i;

题目(未完待续):


3.最长上升子序列

给定一个整数序列A1A2A3….An。求它的一个递增子序列,使子序列的元素个数尽量多,元素不一定要求连续。
考虑以第i个数结尾的最长上升子序列,枚举第i个数之前的所有数,那么它的长度就是以之前小于第i个数的数结尾的最长上升子序列的长度加1。
即:dp[i]=max{dp[j]}+1.(j<i且a[j]<a[i])。
答案是所有的当中最大的。
最长下降/不上升/不下降子序列同理。
时间:O(n2)O(n2)
题目(未完待续):


4.最长公共子序列

一个给定序列的子序列是在该序列中删去若干元素(也可以不删去)后得到的序列。例如Z=”BCDB” 就是X=”ABCBDAB”的一个子序列,而Z=”CBBD”则不是X的子序列。 给定三个序列 X,Y和Z,如果Z既是X的一个子序列又是Y的一个子串,则称Z是X和Y的公共子序列。
例如X=”ABCBDAB”,Y=BDCABA”,则序列”BCA”即为X和Y的一个公共子序列,但不是X和Y的最长公共子序列(LCS),因为还有比它更长的公共子序列”BCBA”。事实上”BCBA”是X和Y的一个LCS ,”BDAB”也是一个LCS。
现输入两个序列X和Y,要求出X和Y的最长公共子序列。
设f[i][j]为X中前i个元素与Y中前j个元素的最长公共子序列的长度。
那么分2种情况。
X[i]=Y[j]时,f[i][j]=f[i-1][j-1]+1.
X[i]≠Y[j]时,f[i][j]=max(f[i-1][j],f[i][j-1]).
题目(未完待续):


5.资源分配(是叫这个吗我不太确定)

n个物品分配有k个物品(这是什么鬼),求出最优的方案。
思路:前i个物品分配有j个物品,则考虑第j个物品放在第1,2,…,i-1个物品之后。
那么,一般就是:dp[i][j]=max{dp[k][j-1]+a[k+1][i]},1≤k≤i-1.
题目(未完待续):


6.区间

就是在一个区间里面要解决一些奇奇怪怪的问题。
思路:dp[i][i+len]表示起点为i长度为len的区间中的最优解。
然后我也不知道怎么描述了!!
题目(未完待续):


然而,dp怎么可能是靠这几个模型就可以做出来的呢?
dp又岂止这么一点模型?
还得好好学啊emmm。

03-08
### 动态规划在计算机科学中的应用 动态规划(Dynamic Programming, DP)是一种用于解决复杂问题的技术,通过将其分解成更简单的子问题来求解最优解[^1]。这种方法特别适用于具有重叠子结构和最优子结构性质的问题。 #### 基本原理 当一个问题可以被划分为多个相互独立的小规模相同问题时,就可以考虑使用动态规划方法。该技术通常涉及两个主要方面: - **记忆化**:存储已经计算过的中间结果以便重复利用; - **状态转移方程**:定义如何从前一步骤的结果推导出下一步骤的结果。 #### 应用实例 ##### 1. 斐波那契数列 斐波那契序列一个经典的例子,在这里展示了如何有效地运用自底向上的迭代方式实现动态规划算法。 ```python def fibonacci(n): if n <= 0: return 0 elif n == 1: return 1 dp = [0] * (n + 1) dp[1] = 1 for i in range(2, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n] ``` ##### 2. 背包问题 背包问题是另一个常见的应用场景,其中涉及到选择物品放入固定容量的背包内以获得最大价值。 ```python def knapsack(weights, values, capacity): n = len(values) # 创建二维数组dp,大小为(n+1)*(capacity+1),并初始化为0 dp = [[0]*(capacity+1) for _ in range(n+1)] for i in range(1,n+1): for w in range(capacity+1): if weights[i-1]<=w: dp[i][w]=max(dp[i-1][w],values[i-1]+dp[i-1][w-weights[i-1]]) else: dp[i][w]=dp[i-1][w] return dp[-1][-1] ``` #### 显示面板(DP接口) 除了上述理论层面的应用外,“DP”也可能指代DisplayPort标准下的显示端口(Display Port)。这是一种高清视频和音频传输协议,广泛应用于现代显示器和其他多媒体设备之间建立连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值