动态规划问题

数据结构、算法总述:数据结构/算法 C/C++-CSDN博客

目录

动态规划(Dynamic Programming,DP)

背包问题

01背包问题

使用滚动数组,倒序遍历优化

完全背包问题 

多重背包问题

二进制拆分优化

单调队列优化

分组背包问题

线性dp

数字三角形

最长上升子序列

二分优化

最长公共子序列

最短编辑距离

区间dp

石子合并

计数类dp

整数划分

数位统计DP

状态压缩DP

蒙德里安的梦想

最短Hamilton路径

树形DP

记忆化搜索


动态规划(Dynamic Programming,DP)

概念:

过将复杂问题分解为简单的子问题来求解决策过程的最优化。

核心思想是分解子问题并存储子问题的答案以减少重复计算。

方法: 

背包问题

  • 01背包每件物品只能装一次
  • 完全背包每件物品可以装无限次
  • 多重背包每件物品只能装有限次(多次)
  • 分组背包每组只能选择一件物品装入(01背包升级)

01背包问题

题目
有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

基本思路
每种物品仅有一件,可以选择放或不放。

#include <iostream>
#include <vector>
using namespace std;

const int N = 1010;
int n, m;
int v[N], w[N]; // 体积和价值数组
int dp[N][N]; // dp[i][j] 表示考虑前i件物品,且背包容量为j时的最大价值

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    // 初始化dp数组,可以不初始化,因为下面会覆盖
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            dp[i][j] = 0;
        }
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (v[i] > j) {
                dp[i][j] = dp[i-1][j]; // 不装第i件物品
            } else {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]] + w[i]); // 装或不装第i件物品,取最大值
            }
        }
    }
    
    cout << dp[n][m] << endl; // 输出最大价值
    return 0;
}
使用滚动数组,倒序遍历优化
#include <iostream>
using namespace std;

const int N = 1010;
int n, m;
int v[N], w[N]; // v代表体积,w代表价值
int dp[N];

int main() {
    cin >> n >> m;
    
    for (int i = 1; i <= n; i++) {
        // 省略了cin语句,因为我们已经提供了示例值
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= v[i]; j--) { // j代表背包容量,滚动数组必须倒序遍历
            dp[j] = max(dp[j], dp[j - v[i]] + w[i]); // 滚动数组
        }
    }
    
    cout << dp[m] << endl; // 输出最后的一个一定是最大的
    return 0;
}

状态转移方程:

f[i][j] = max(f[i - 1][j], f[i - 1][j - v] + w)

完全背包问题 

题目
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

基本思路
每件物品可以装无限次

#include <iostream>
using namespace std;

const int N = 1010;
int v[N], w[N];
int dp[N];

int main() {
    int n, m;
    cin >> n >> m;
    
    // 初始化v[i]和w[i]
    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> w[i];
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = v[i]; j <= m; j++) {
            dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
        }
    }
    
    cout << dp[m] << endl;
    return 0;
}
状态转移方程:
f[i][j] = max(f[i - 1][j], f[i][j - v] + w)

多重背包问题

题目
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件体积是v[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

基本思路
多重背包每件物品只能装有限次(多次)

#include <iostream>
using namespace std;
const int N = 1100;
int f[N][N], v[N], w[N], s[N];

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> w[i] >> s[i];
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= 0; j--) { // 从大到小遍历背包容量
            for (int k = 0; k <= s[i] && k * v[i] <= j; k++) {
                f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
            }
        }
    }
    
    cout << f[n][m] << endl;
    return 0;
}
状态转移方程:
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i] * k] + w[i] * k);//k为第i个物品的个数
二进制拆分优化
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 12010, M = 2010;
int n, m;
int v[N], w[N];
int f[M];

int main()
{
    cin >> n >> m;

    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        int a, b, s;
        cin >> a >> b >> s;
        int k = 1;
        while (k <= s)
        {
            cnt ++ ;
            v[cnt] = a * k;
            w[cnt] = b * k;
            s -= k;
            k *= 2;
        }
        if (s > 0)
        {
            cnt ++ ;
            v[cnt] = a * s;
            w[cnt] = b * s;
        }
    }

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

    cout << f[m] << endl;
    return 0;
}
单调队列优化
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 20010;
int f[N], g[N], q[N];
int n, m;
int v, w, s;

int main() 
{
    cin >> n >> m;
    for (int i = 0; i < n; ++i) 
    {
        cin >> v >> w >> s;
        memcpy(g, f, sizeof f);
        for (int j = 0; j < v; ++j) 
        {
            int hh = 0, tt = -1;
            for (int k = j; k <= m; k += v) 
            {
                if (hh <= tt && k - s * v > q[hh])  hh++;
                while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w)  tt--;
                q[++tt] = k;
                f[k] = g[q[hh]] + (k - q[hh]) / v * w;
            }
        }
    }
    cout << f[m] << endl;
    return 0;
}

分组背包问题

题目
有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

基本思路
每组只能选择一件物品装入

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 110;
int f[N];
int v[N][N], w[N][N], s[N];
int n, m, k;

int main() 
{
    cin >> n >> m;
    for (int i = 0; i < n; i++) 
    {
        cin >> s[i];
        for (int j = 0; j < s[i]; j++)
            cin >> v[i][j] >> w[i][j];
    }
    for (int i = 0; i < n; i++) 
    {
        for (int j = m; j >= 0; j--) 
        {
            for (int k = 0; k < s[i]; k++)  //for(int k=s[i];k>=1;k--)也可以
            {
                if (j >= v[i][k])  f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
            }
        }
    }
    cout << f[m] << endl;
}

状态转移方程:

f[j] = max(f[j], f[j - v[i][k]] + w[i][k])

线性dp

数字三角形

题目
给定一个数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

#include<iostream>
using namespace std;

// 定义常量N为矩阵的大小,INF为极大值,这里设为1e9,即10^9
const int N=510,INF=1e9;
int n;
int a[N][N];
int f[N][N];

int main()
{
    scanf("%d",&n);
    // 读入矩阵a的值,注意只读入下三角的部分,因为矩阵是对称的
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);

    // 初始化动态规划数组f,对角线和边界设为极大值INF
    for(int i=0;i<=n;i++)
        for(int j=0;j<=i+1;j++)
            f[i][j]=-INF;

    f[1][1]=a[1][1];

    // 动态规划求解,更新f数组
    for(int i=2;i<=n;i++)
        for(int j=1;j<=i;j++)
            f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);

    int res=-INF;

    for(int i=1;i<=n;i++)
        res=max(res,f[n][i]);

    printf("%d",res);
    return 0;
}

状态转移方程:

f[i][j] = max(f[i-1][j-1]+a[i][j], f[i-1][j]+a[i][j])

最长上升子序列

题目
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

#include<iostream>
using namespace std;

const int N = 1010;
int n;
int a[N], f[N];

int main() 
{
    scanf("%d", &n);

    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);

    for (int i = 1; i <= n; i++) 
    {
        f[i] = 1; // 只有a[i]一个数
        for (int j = 1; j <= i; j++)
            if (a[j] < a[i])
                f[i] = max(f[i], f[j] + 1);
    }
    int res = 0;

    for (int i = 1; i <= n; i++)
        res = max(res, f[i]);

    printf("%d\n", res);
    return 0;
}
状态转移方程:
if(a[j] < a[i]) f[i] = max(f[i], f[j] + 1)
二分优化
#include<iostream>
using namespace std;

const int N = 100010;
int n;
int a[N];
int q[N];

int main() 
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);

    int len = 0;

    for (int i = 0; i < n; i++) 
    {
        int l = 0, r = len;

        while (l < r) 
        {
            int mid = l + r + 1 >> 1;
            if (q[mid] < a[i])
                l = mid;
            else
                r = mid - 1;
        }
        len = max(len, r + 1);

        q[r + 1] = a[i]; // 替换或添加
    }
    printf("%d\n", len);

    return 0;
}

最长公共子序列

题目
给定两个长度分别为 N 和 M 的字符串 A 和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少

#include<iostream>
using namespace std;

const int N = 1010;
int n, m;
char a[N], b[N];
int f[N][N];

int main() 
{
    cin >> n >> m >> a + 1 >> b + 1;

    for (int i = 1; i <= n; i++) 
    {
        for (int j = 1; j <= m; j++) 
        {
            f[i][j] = max(f[i - 1][j], f[i][j - 1]);
            if (a[i] == b[j])
                f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

状态转移方程:

f[i][j] = max(f[i-1][j], f[i][j-1]);
if(a[i] == b[j]) f[i][j] = max(f[i][j], f[i-1][j-1] + 1);

最短编辑距离

题目
给定两个字符串 A 和 B,现在要将 A 经过若干操作变为 B,可进行的操作有:

  1. 删除–将字符串 A 中的某个字符删除。
  2. 插入–在字符串 A 的某个位置插入某个字符。
  3. 替换–将字符串 A 中的某个字符替换为另一个字符。

现在请你求出,将 A 变为 B 至少需要进行多少次操作。

#include<iostream>
using namespace std;

const int N = 1010;
int n, m;
char a[N], b[N];
int f[N][N];

int main() 
{
    scanf("%d%s", &n, a + 1);
    scanf("%d%s", &m, b + 1);

    for (int i = 0; i <= m; i++)
        f[0][i] = i;

    for (int i = 0; i <= n; i++)
        f[i][0] = i; // 初始化字符串的编辑操作

    for (int i = 1; i <= n; i++) 
    {
        for (int j = 1; j <= m; j++) 
        {
            f[i][j] = min(f[i - 1][j] + 1, f[i][j - 1] + 1);

            if (a[i] == b[j])
                f[i][j] = min(f[i][j], f[i - 1][j - 1]);
            else
                f[i][j] = min(f[i][j], f[i - 1][j - 1] + 1); // 状态转移方程
        }
    }

    printf("%d\n", f[n][m]);
    return 0;
}

状态转移方程:

f[i][j] = min(f[i-1][j]+1, f[i][j-1]+1);
if(a[i] == b[j]) f[i][j] = min(f[i][j], f[i-1][j-1]);
else f[i][j] = min(f[i][j], f[i-1][j-1]+1);

区间dp

石子合并

题目
每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子 将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

#include<iostream>
using namespace std;

const int N = 310;
int n;
int s[N];
int f[N][N]; // 状态表示:集合f[l][r]为[l,r]区间;属性:所堆成的最小值

int main() 
{
    scanf("%d", &n);

    for (int i = 1; i <= n; i++)
        scanf("%d", &s[i]);

    for (int i = 1; i <= n; i++)
        s[i] += s[i - 1];              // 前缀和用来求一段区间的和

    for (int len = 2; len <= n; len++) // 区间长度为len//枚举长度
        for (int i = 1; i + len - 1 <= n; i++)  // 意思就是i在区间[1,n-len+1]中去//枚举区间
        {
            int l = i, r = i + len - 1; // 区间在[i,i+len-1]中间长度为len//设置l和r的区间

            f[l][r] = 1e9;              // 初始化最大值

            for (int k = l; k < r; k++) // 枚举分界点//不取r
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
        }
    printf("%d\n", f[1][n]);
    return 0;
}

状态转移方程为:

f[l][r] = min(f[l][r], f[l][k] + f[k+1][r] + s[r]-s[l-1])

计数类dp

整数划分

题目
一个正整数 n 可以表示成若干个正整数之和,我们将这样的一种表示称为正整数 n 的一种划分。 现在给定一个正整数 n,请你求出 n共有多少种不同的划分方法。

#include <iostream>
using namespace std;

const int M = 1e9 + 7;
int f[1010], n;

int main() 
{
    cin >> n;
    f[0] = 1;

    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++) 
            f[j] = (f[j - i] + f[j]) % M;

    cout << f[n] << endl;
    return 0;
}

状态转移方程:

f[j] = (f[j-i] + f[j])

数位统计DP

题目
给定两个整数 a 和 b,求 a 和 b 之间的所有数字中 0∼9的出现次数。

#include<iostream>
#include<cmath>
using namespace std;

int dgt(int n) // 计算整数n有多少位
{
    int res = 0;
    while (n) ++ res, n /= 10;
    return res;
}

int cnt(int n, int i) // 计算从1到n的整数中数字i出现多少次 
{
    int res = 0, d = dgt(n);
    for (int j = 1; j <= d; ++ j) // 从右到左第j位上数字i出现多少次
    {
        // l和r是第j位左边和右边的整数; dj是第j位的数字
        int p = pow(10, j - 1), l = n / p / 10, r = n % p, dj = n / p % 10;
        // 计算第j位左边的整数小于l的情况
        if (i) res += l * p; 
        if (!i && l) res += (l - 1) * p; // 如果i = 0, 左边高位不能全为0
        // 计算第j位左边的整数等于l情况
        if ( (dj > i) && (i || l) ) res += p;
        if ( (dj == i) && (i || l) ) res += r + 1;
    }
    return res;
}

int main()
{
    int a, b;
    while (cin >> a >> b , a)
    {
        if (a > b) swap(a, b);
        for (int i = 0; i <= 9; ++ i) cout << cnt(b, i) - cnt(a - 1, i) << ' ';
        cout << endl;
    }
    return 0;
}

状态压缩DP

蒙德里安的梦想

题目
n×m 的棋盘可以摆放不同的1×2小方格的种类数。

#include<iostream>
#include<cstring>
using namespace std;

const int N = 12, M = 1 << N;
int n, m;
long long f[N][M];
bool st[M];

int main() 
{
    int n, m;
    while (cin >> n >> m, n || m) 
    {
        memset(f, 0, sizeof f);
        // 预处理:判断合并列的状态i是否合法
        // 如果合并列的某行是1表示横放,是0表示竖放
        // 如果合并列不存在连续的奇数个0,即为合法状态
        for (int i = 0; i < 1 << n; i++) 
        {
            st[i] = true;
            int cnt = 0; // 记录合并列中连续0的个数
            for (int j = 0; j < n; j++) 
            {
                if (i >> j & 1)    // 如果是1
                {
                    if (cnt & 1)   // 如果连续0的个数是奇数
                    {
                        st[i] = false; // 记录i不合法
                        break;
                    }
                } else
                    cnt++; // 如果是0,记录0的个数
            }
            if (cnt & 1)
                st[i] = false; // 处理高位0的个数
        }
        // 状态计算
        f[0][0] = 1;                 // 第0列不横放是一种合法的方案
        for (int i = 1; i <= m; i++) // 阶段:枚举列
            for (int j = 0; j < 1 << n; j++)     // 状态:枚举i列的状态
                for (int k = 0; k < 1 << n; k++) // 状态:枚举i-1列的状态
                    // 两列状态兼容:不出现重叠的1,不出现连续奇数个0
                    if ((j & k) == 0 && st[j | k])
                        f[i][j] += f[i - 1][k];
        cout << f[m][0] << endl; // 第m列不横放,既答案
    }
    return 0;
}

状态转移方程:

if((j & k) == 0 && st[j|k])  f[i][j] += f[i-1][k];
最短Hamilton路径

题目
给定一张 n 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

#include<iostream>
#include<cstring>
using namespace std;

const int N = 20, M = 1 << N;
int n;
int w[N][N];
int f[M][N]; // 第一维表示是否访问到该点的压缩状态,第二维是走到点j
             // f[i][j]表示状态为i并且到j的最短路径
int main() 
{
    cin >> n;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) // 读入i到j的距离
            cin >> w[i][j];
            
    memset(f, 0x3f, sizeof f);
    f[1][0] = 0;
    
    for (int i = 0; i < 1 << n; i++)        // 枚举压缩的状态
        for (int j = 0; j < n; j++)         // 枚举到0~j的点
            if (i >> j & 1)                 // 该状态存在j点
                for (int k = 0; k < n; k++) // 枚举从j倒数第二个点k
                    if (i >> k & 1)         // 倒数点k存在
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] +w[k][j]); // 状态转移方程,在f[i][j] 和状态去掉j的点f[i - (i << j)][k] + w[k][j] 取最小值 
    
    cout<< f[(1 << n) - 1][n - 1]<< endl; // 输出状态全满也就是所有点都经过且到最后一个点的最短距离
    return 0;
}

状态转移方程:

f[i][j] = min(f[i][j], f[i-(1<<j)][k] + w[k][j]);

树形DP

题目
Ural 大学有 N 名职员,编号为 1∼N。
他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。
每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。
现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。
在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。

#include <iostream>
#include <vector>

using namespace std;
const int N = 6010;
int h[N];
int v[N];
vector<int> son[N];
int f[N][2];

void dp(int x) 
{
    f[x][0] = 0;
    f[x][1] = h[x];
    for (int i = 0; i < son[x].size(); i++) 
    {
        int y = son[x][i];
        dp(y);
        f[x][0] += max(f[y][0], f[y][1]);
        f[x][1] += f[y][0];
    }
}

int main() 
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> h[i];
    for (int i = 1; i <= n - 1; i++) 
    {
        int x, y;
        cin >> x >> y;
        son[y].push_back(x);
        v[x] = 1;
    }
    int root;
    for (int i = 1; i <= n; i++)
        if (!v[i]) 
        {
            root = i;
            break;
        }
    dp(root);
    cout << max(f[root][0], f[root][1]) << endl;
    return 0;
}

记忆化搜索

题目
给定一个R 行 C 列的矩形网格滑雪场
一个人从滑雪场中的某个区域内出发,每次可以向上下左右任意一个方向滑动一个单位距离
当然,一个人能够滑动到某相邻区域的前提是该区域的高度低于自己目前所在区域的高度

#include<iostream>
#include<cstring>
using namespace std;

const int N = 310;
int n, m;
int h[N][N];
int f[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int dp(int x, int y) 
{
    int& v = f[x][y];
    if (v != -1)  return v; // 记忆化搜索核心
    v = 1;

    for (int i = 0; i < 4; i++) 
    {
        int a = x + dx[i], b = y + dy[i];
        if (a >= 1 && a <= n && b >= 1 && b <= m && h[a][b] < h[x][y]) // 判断是否越界且上一个经过的点的高度是否大于当前高度
            v = max(v, dp(a, b) + 1);
    }
    return v;
}
int main() 
{
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d", &h[i][j]);

    memset(f, -1, sizeof f);
    int res = 0;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            res = max(res, dp(i, j));

    printf("%d\n", res);
    return 0;
}

状态转移方程

v = max(v, dp(a,b) + 1)

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
动态规划(Dynamic Programming)是一种常用的优化问题求解方法,而MATLAB是一个功能强大的数值计算和编程软件,也提供了丰富的工具和函数来解决动态规划问题。 在MATLAB中,我们可以使用矩阵运算和递推关系来实现动态规划算法。具体步骤如下: 1. 定义问题的状态和状态转移方程:首先我们需要将原始问题转化为一组状态和状态之间的转移关系。例如,在背包问题中,状态可以是当前背包容量和选择的物品数量,状态之间的转移关系可以是选择当前物品或不选择。 2. 创建动态规划数组:根据问题的状态数和状态转移方程,我们可以创建一个二维数组或多维数组来存储中间结果。这个数组将用于存储每个状态下的最优解值。 3. 初始化数组:将数组的初始状态设置为问题的已知状态,例如,在背包问题中,初始化数组的第一行和第一列为0,表示背包容量为0或选择的物品数量为0时,最优值为0。 4. 递推计算最优值:使用循环来填充动态规划数组的剩余元素,根据状态转移方程和已知的中间结果来计算每个状态的最优值。例如,在背包问题中,使用嵌套循环遍历背包容量和选择的物品数量的所有可能取值,根据当前物品的重量和价值,以及之前的中间结果,计算出当前状态下的最优值。 5. 回溯得到最优解:通过分析动态规划数组,可以得到最优解。例如,在背包问题中,可以从最后一个状态开始,根据中间结果和状态转移方程依次决定选择哪个物品。 总而言之,通过MATLAB的矩阵运算和递推关系,我们可以有效地解决动态规划问题。实现时需要注意问题的状态和状态转移方程的定义,以及数组的初始化和递推计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

禊月初三

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

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

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

打赏作者

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

抵扣说明:

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

余额充值