动态规划题目练习

这篇具有很好参考价值的文章主要介绍了动态规划题目练习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基础知识:

动态规划背包问题-CSDN博客

动态规划基础概念-CSDN博客

题目练习:

 题目1:过河卒

题目描述

棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐标是需要给出的。

动态规划题目练习,动态规划,算法

现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 B 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

输入输出样例

输入 #1复制

6 6 3 3

输出 #1复制

6

说明/提示

对于 100% 的数据,1≤n,m≤20,0≤马的坐标≤20。

【题目来源】

NOIP 2002 普及组第四题

代码加注释:

#include<stdio.h>  
#include<string.h>  
  
// 定义一个二维数组dp,用于存储到达每个位置的不同路径数  
long long int dp[100][100];  
// 定义一个二维数组s,用于标记障碍物位置  
bool s[100][100];  
  
int main() {  
	// 初始化dp数组为0  
	memset(dp, 0, sizeof(dp));  
	  
	int x, y, x1, y1;  
	// 输入起始点和终点坐标  
	scanf("%d%d%d%d", &x, &y, &x1, &y1);  
	  
	// 将坐标值加2,这样坐标(1,1)在数组中的位置就是(3,3),以便在数组边界外有空间  
	x += 2, y += 2, x1 += 2, y1 += 2;  
	  
	// 定义八个方向的偏移量,用于标记障碍物周围的位置  
	int next[8][2] = { {1,2},{1,-2},{2,1},{2,-1},{-1,-2},{-2,-1},{-1,2},{-2,1} };  
	  
	// 将终点标记为障碍物  
	s[x1][y1] = 1;  
	  
	// 将终点周围八个方向的位置也标记为障碍物  
	for (int i = 0; i < 8; i++) {  
		s[x1 + next[i][0]][y1 + next[i][1]] = 1;  
	}  
	  
	// 初始化起点位置到起点的路径数为1  
	dp[2][1] = 1;  
	  
	// 动态规划计算到达每个位置的不同路径数  
	for (int i = 2; i <= x; i++) {  
		for (int j = 2; j <= y; j++) {  
			// 如果当前位置是障碍物,则跳过  
			if (s[i][j]) continue;  
			// 当前位置的不同路径数等于它左边和上边位置的不同路径数之和  
			dp[i][j] = dp[i][j - 1] + dp[i - 1][j];  
		}  
	}  
	  
	// 输出从起点到终点的不同路径数  
	printf("%lld", dp[x][y]);  
  
	return 0;  
}
//注意:

//代码中使用x += 2, y += 2, x1 += 2, y1 += 2是为了让实际坐标映射到数组dp和s时留有边界空间,
//防止数组越界。
//dp[i][j]保存的是到达位置(i, j)的不同路径数。
//s[i][j]用于标记障碍物位置,其中true表示是障碍物,false表示不是障碍物。
//动态规划过程中,通过累加左边和上边位置的路径数来更新当前位置的路径数。

 题目2:装箱问题

题目描述

有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积。

现在从 n 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。

输入格式

第一行共一个整数 V,表示箱子容量。

第二行共一个整数 n,表示物品总数。

接下来 n 行,每行有一个正整数,表示第 i 个物品的体积。

输出格式

共一行一个整数,表示箱子最小剩余空间。

输入输出样例

输入 #1复制

24
6
8
3
12
7
9
7

输出 #1复制

0

说明/提示

对于 100%100% 数据,满足0<n≤301≤V≤20000。

【题目来源】

NOIP 2001 普及组第四题

代码加解析:

#include<stdio.h>  // 引入标准输入输出库  
#include<iostream> // 引入输入输出流库,但实际上代码中并未使用iostream的功能  
using namespace std; // 使用标准命名空间  
  
int a[100]; // 定义一个整型数组a,用于存储n个物品的价值  
int dp[20100]; // 定义一个整型数组dp,用于存储容量为j时的最大价值  
  
int main()  
{  
    int V, n; // 定义两个整型变量V和n,分别表示背包的总容量和物品的数量  
    scanf("%d%d", &V, &n); // 输入背包的总容量V和物品的数量n  
  
    for (int i = 1; i <= n; i++) {  
        scanf("%d", &a[i]); // 输入每个物品的价值,并存储在数组a中  
    }  
  
    dp[0] = 0; // 初始化dp数组,当容量为0时,最大价值为0  
  
    // 动态规划的主体部分  
    for (int i = 1; i <= n; i++) { // 遍历每个物品  
        for (int j = V; j >= a[i]; j--) { // 遍历背包的每个可能容量,从大到小遍历是为了保证每个物品只被选取一次  
            dp[j] = max(dp[j], dp[j - a[i]] + a[i]); // 更新dp数组,如果当前物品能放入背包并且加上当前物品后的总价值更大,则更新dp[j]  
        }  
    }  
  
    printf("%d", V - dp[V]); // 输出剩余容量,即总容量V减去最大价值dp[V],即表示背包装不下的物品总价值  
  
    return 0; // 主函数返回0,表示程序正常结束  
}

 题目3:小A点菜

题目背景

uim 神犇拿到了 uoi 的 ra(镭牌)后,立刻拉着基友小 A 到了一家……餐馆,很低端的那种。

uim 指着墙上的价目表(太低级了没有菜单),说:“随便点”。

题目描述

不过 uim 由于买了一些书,口袋里只剩 M 元 (M≤10000)。

餐馆虽低端,但是菜品种类不少,有 N 种 ((N≤100),第 i 种卖 ai​ 元 (ai​≤1000)。由于是很低端的餐馆,所以每种菜只有一份。

小 A 奉行“不把钱吃光不罢休”,所以他点单一定刚好把 uim 身上所有钱花完。他想知道有多少种点菜方法。

由于小 A 肚子太饿,所以最多只能等待 11 秒。

输入格式

第一行是两个数字,表示 N 和 M。

第二行起 N 个正数 ai​(可以有相同的数字,每个数字均在 10001000 以内)。

输出格式

一个正整数,表示点菜方案数,保证答案的范围在 int 之内。

输入输出样例

输入 #1复制

4 4
1 1 2 2

输出 #1复制

3

说明/提示

2020.8.29,增添一组 hack 数据 by @yummy

代码加详细解析:

#include<stdio.h>  
  
// 定义两个数组,a用于存储菜品的价格,dp用于存储动态规划的状态  
int a[200];  
int dp[200][10000];  
  
int main()  
{  
	int n, m;  
	scanf("%d%d", &n, &m); // 输入菜品的种类数n和拥有的钱数m  
  
	// 读取每种菜品的价格  
	for (int i = 1; i <= n; i++) {  
		scanf("%d", &a[i]);  
	}  
  
	// 初始化dp数组,dp[0][0]表示没有菜品可选且钱数为0时的方法数为1(不选任何菜品)  
	dp[0][0] = 1;  
  
	// 动态规划的主体部分  
	for (int i = 1; i <= n; i++) { // 遍历每种菜品  
		for (int j = 0; j <= m; j++) { // 遍历当前拥有的钱数  
			// 如果当前钱数大于菜品价格  
			if (j >= a[i]) {  
				// 则可以选择购买当前菜品,方法数等于不购买当前菜品的方法数加上购买当前菜品后剩余钱数的方法数  
				dp[i][j] = dp[i - 1][j] + dp[i - 1][j - a[i]];  
			} else {  
				// 如果当前钱数小于菜品价格,则不能购买当前菜品,方法数等于不购买当前菜品的方法数  
				dp[i][j] = dp[i - 1][j];  
			}  
		}  
	}  
  
	// 输出在拥有m元钱时,可以选择的菜品组合方法数  
	printf("%d", dp[n][m]);  
  
	return 0;  
}

题目4:考前临时抱佛脚

题目背景

kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。

题目描述

这次期末考试,kkksc03 需要考 44 科。因此要开始刷习题集,每科都有一个习题集,分别有 s1​,s2​,s3​,s4​ 道题目,完成每道题目需要一些时间,可能不等(A1​,A2​,…,As1​​,B1​,B2​,…,Bs2​​,C1​,C2​,…,Cs3​​,D1​,D2​,…,Ds4​​)。

kkksc03 有一个能力,他的左右两个大脑可以同时计算 22 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。

由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。

输入格式

本题包含 55 行数据:第 11 行,为四个正整数 s1​,s2​,s3​,s4​。

第 22 行,为 A1​,A2​,…,As1​​ 共 s1​ 个数,表示第一科习题集每道题目所消耗的时间。

第 33 行,为 B1​,B2​,…,Bs2​​ 共 s2​ 个数。

第 44 行,为 C1​,C2​,…,Cs3​​ 共 s3​ 个数。

第 55 行,为 D1​,D2​,…,Ds4​​ 共 s4​ 个数,意思均同上。

输出格式

输出一行,为复习完毕最短时间。

输入输出样例

输入 #1复制

1 2 1 3		
5
4 3
6
2 4 3

输出 #1复制

20

说明/提示

1≤s1​,s2​,s3​,s4​≤20。

1≤A1​,A2​,…,As1​​,B1​,B2​,…,Bs2​​,C1​,C2​,…,Cs3​​,D1​,D2​,…,Ds4​​≤60。

代码加解析:

#include<stdio.h> // 引入标准输入输出库  
#include<iostream> // 引入标准输入输出流库  
using namespace std; // 使用标准命名空间  
  
// 定义全局变量  
int a[5]; // 数组,用于存储四组习题集的数量  
int sum; // 变量,用于计算每一组习题集的总时间  
int t; // 变量,用于存储一共所需的总时间  
int h[21]; // 数组,用于存储每个习题集的完成所需时间  
int dp[2051]; // 动态规划数组,用于存储中间结果  
  
int main() {  
    // 读取四组习题集的数量  
    for (int i = 1; i <= 4; i++) {  
        scanf("%d", &a[i]); // 从标准输入读取每组习题集的数量  
    }  
  
    // 处理每组习题集  
    for (int i = 1; i <= 4; i++) {  
        sum = 0; // 初始化sum为0,用于计算当前组习题集的总时间  
          
        // 读取当前组每个习题集的完成所需时间,并计算总时间  
        for (int j = 1; j <= a[i]; j++) {  
            scanf("%d", &h[j]); // 从标准输入读取当前习题集的完成所需时间  
            sum += h[j]; // 累加当前习题集的完成时间到sum  
        }  
  
        // 动态规划处理当前组习题集  
        for (int j = 1; j <= a[i]; j++) {  
            for (int k = sum / 2; k >= h[j]; k--) {  
                // 更新dp[k]为考虑第j个习题集时的最大和  
                dp[k] = max(dp[k], dp[k - h[j]] + h[j]);  
            }  
        }  
  
        // 计算当前组习题集的最大差值,并累加到总时间t  
        t += sum - dp[sum / 2];  
  
        // 重置dp数组,为下一组习题集做准备  
        for (int j = 1; j <= sum / 2; j++) {  
            dp[j] = 0;  
        }  
    }  
  
    // 输出总时间t  
    printf("%d", t);  
  
    return 0; // 程序正常结束  
}

题目5:挖地雷

 题目描述

在一个地图上有 N\(N <= 20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

输入格式

有若干行。

第 1行只有一个数字,表示地窖的个数 N。

第 2 行有 N 个数,分别表示每个地窖中的地雷个数。

第 3 行至第 N+1 行表示地窖之间的连接情况:

第 3 行有 n-1 个数(0 或 1),表示第一个地窖至第 2 个、第 3 个 \dots 第 n 个地窖有否路径连接。如第 3 行为 11000\cdots 0,则表示第 1个地窖至第 2 个地窖有路径,至第 3 个地窖有路径,至第 4 个地窖、第 5 个 \dots 第 n 个地窖没有路径。

第 4 行有 n-2 个数,表示第二个地窖至第 3 个、第 4 个 \dots 第 n 个地窖有否路径连接。

……

第 n+1 行有 1 个数,表示第 n-1 个地窖至第 n 个地窖有否路径连接。(为 $0$ 表示没有路径,为 $1$ 表示有路径)。

 输出格式

第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。

第二行只有一个数,表示能挖到的最多地雷数。

输入输出样例

输入 #1复制

5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1

输出 #1复制

1 3 4 5
27

说明/提示

【题目来源】

NOIP 1996 提高组第三题

代码加注释:

#include<iostream>  
using namespace std;  
  
// 定义二维数组L,用于存储图的邻接矩阵  
int L[24][24];   
// 定义数组a,用于存储每个节点的权值  
int a[24];  
// 定义数组p,用于记录当前搜索路径上的点  
int p[24], ans[24], cnt;  
// 定义数组ans,用于记录最优路径上的点  
// 定义变量cnt,用于记录最优路径的长度  
// 定义布尔数组b,用于标记每个点是否已经被访问过  
bool b[24];  
// 定义变量n,表示节点的数量  
int n;  
// 定义变量maxx,用于存储最大权值和  
int maxx;  
  
// 函数ch:检查节点x是否可以作为路径的终点  
bool ch(int x) {  
    for (int i = 1; i <= n; i++) {  
        // 如果节点x与节点i之间有边相连且节点i未被访问过,则返回false  
        if (L[x][i] && !b[i])return false;  
    }  
    // 如果没有边相连或所有相连节点都已被访问过,则返回true  
    return true;  
}  
  
// 函数dfs:深度优先搜索  
void dfs(int x, int s, int sum) {  
    // 如果节点x可以作为路径的终点  
    if (ch(x)) {  
        // 如果当前路径的权值和大于已知的最大权值和  
        if (sum > maxx) {  
            // 更新最大权值和  
            maxx = sum;  
            // 更新最优路径的长度  
            cnt = s;  
            // 更新最优路径上的点  
            for (int i = 1; i <= n; i++) {  
                ans[i] = p[i];  
            }  
        }  
        // 结束搜索  
        return;  
    }  
    // 遍历与节点x相邻的节点  
    for (int i = 1; i <= n; i++) {  
        // 如果节点x与节点i之间有边相连且节点i未被访问过  
        if (L[x][i] && !b[i]) {  
            // 标记节点i为已访问  
            b[i] = 1;  
            // 将节点i添加到当前搜索路径中  
            p[s + 1] = i;  
            // 递归搜索节点i  
            dfs(i, s + 1, sum + a[i]);  
            // 回溯,标记节点i为未访问  
            b[i] = 0;  
        }  
    }  
}  
  
int main() {  
    // 输入节点数量  
    cin >> n;  
    // 输入每个节点的权值  
    for (int i = 1; i <= n; i++) {  
        cin >> a[i];  
    }  
    // 输入图的邻接矩阵  
    for (int i = 1; i < n; i++) {  
        for (int j = i + 1; j <= n; j++) {  
            cin >> L[i][j];  
            // 注意:这里只输入了上三角矩阵,因此还需要将下三角矩阵置为相同值,表示无向图  
            L[j][i] = L[i][j];  
        }  
    }  
    // 从每个节点开始搜索最优路径  
    for (int i = 1; i <= n; i++) {  
        // 标记节点i为已访问  
        b[i] = 1;  
        // 将节点i作为搜索路径的起点  
        p[1] = i;  
        // 搜索最优路径  
        dfs(i, 1, a[i]);  
        // 回溯,标记节点i为未访问  
        b[i] = 0;  
    }  
    // 输出最优路径上的点  
    for (int i = 1; i <= cnt; i++) {  
        printf("%d ", ans[i]);  
    }  
    printf("\n");  
    // 输出最大权值和  
    printf("%d", maxx);  
    return 0;  
}

题目会隔几天更新一次,一直到对这个知识点熟练为止。文章来源地址https://www.toymoban.com/news/detail-858014.html

到了这里,关于动态规划题目练习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法设计与分析】动态规划-练习题

    输入一个整数数组 S[n] ,计算其最长递增子序列的长度,及其最长递增子序列。 定义 k ( 1 ≤ k ≤ n ) k (1 ≤ k ≤ n) k ( 1 ≤ k ≤ n ) ,L[k]表示以 S[k] 结尾的递增子序列的最大长度。子问题即为 L[k]。 对于每一个k,我们都遍历前面0~k-1的所有的数,找出最大的L[i],且 S [ k ] L [

    2024年02月03日
    浏览(58)
  • 五种基础算法小结与典型题目分享(动态规划、分治、贪心、回溯、分支限界)

    动态规划是用于解决多阶段决策问题的算法策略。它通过用变量集合描述当前情境来定义“状态”,进而用这些状态表达每个阶段的决策。 每个阶段的状态是基于前面的状态经过某种决策得到的。通过建立状态间的递推关系,并将其形式化为数学递推式,得到“状态转移方程

    2024年01月19日
    浏览(64)
  • 算法练习Day30 (Leetcode/Python-动态规划)

    62. Unique Paths There is a robot on an  m x n  grid. The robot is initially located at the  top-left corner  (i.e.,  grid[0][0] ). The robot tries to move to the  bottom-right corner  (i.e.,  grid[m - 1][n - 1] ). The robot can only move either down or right at any point in time. Given the two integers  m  and  n , return  the number of possible

    2024年01月20日
    浏览(42)
  • 动态规划2:题目

    目录 第1题 Fibonacci 第2题 字符串分割(Word Break) .第3题 三角矩阵(Triangle) 第4题 路径总数(Unique Paths) 第5题 最小路径和(Minimum Path Sum) 第6题 背包问题 第7题 回文串分割(Palindrome Partitioning) 第8题 编辑距离(Edit Distance) 第9题 不同子序列(Distinct Subsequences) 分析问题: 1. 状态定义F(i):第

    2024年02月06日
    浏览(43)
  • 【LeetCode】动态规划类题目详解

    所有题目均来自于LeetCode,刷题代码使用的Python3版本 如果某一个问题有重叠的子问题,则使用动态规划进行求解是最有效的。 动态规划中每一个状态一定是由上一个状态推导出来的,这一点区别于贪心算法 动态规划五部曲 确定dp数组以及下标的含义 确定递推公式 dp数组如何

    2024年04月11日
    浏览(48)
  • 代码随想录 动态规划-基础题目

    目录 509.斐波那契数  70.爬楼梯 746.使用最小花费爬楼梯 62.不同路径 63.不同路径|| 343.整数拆分 96.不同的二叉树 509. 斐波那契数 简单 斐波那契数  (通常用  F(n)  表示)形成的序列称为  斐波那契数列  。该数列由  0  和  1  开始,后面的每一项数字都是前面两项数字的和

    2024年03月18日
    浏览(76)
  • 一维动态规划经典力扣题目(一)

    目录 题一:斐波那契数列 题目二:最低票价 题三:解码方法 递归方法是2的n次方的时间复杂度。 递归代码: 带有缓存的递归,会使时间复杂度得到大幅度优化。 时间复杂度为O(n)。 缓存即记录中间值         优化的方法:         代码:         代码:

    2024年02月21日
    浏览(41)
  • LeetCode练习七:动态规划上:线性动态规划

    参考《OI Wiki动态规划》、《算法通关手册》动态规划篇 1.1 动态规划简介    动态规划(Dynamic Programming) :简称 DP ,是一种通过把原问题分解为相对简单的子问题的方式而求解复杂问题的方法。 动态规划方法与分治算法类似,却又不同于分治算法。 「动态规划的核心思想

    2024年02月12日
    浏览(69)
  • 【leetcode 力扣刷题】回文串相关题目(KMP、动态规划)

    题目链接:5. 最长回文子串 题目内容: 题目就是要我们找s中的回文子串,还要是最长的。其实想想,暴力求解也行……就是遍历所有的子串,同时判断是不是回文串,是的话再和记录的最大长度maxlen比较,如果更长就更新。时间复杂度直接变成O(n^3)。 优化的点在于,假设子

    2024年02月09日
    浏览(48)
  • 题解动态规划:蓝桥杯2022国赛B组 题解 A题目

    在这组题(蓝桥杯C/C++ B组 国赛)里面挑了几道喜欢的题目,做了一下,笔记思路如下。( 其实是我觉得能做出的题 ) 题目图片来源于:CSDN 罚时大师月色 请问2022,拆分成10个不同的正整数有多少种不同的分法。 这道题目,拿到手上的时候,第一个想法是暴力,但是,每次

    2023年04月08日
    浏览(94)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包