【No.16】蓝桥杯动态规划下|线性DP装箱问题|计数DP0/1背包的方案数|过河卒|完全背包小明的背包2|最长公共子序列|蓝桥骑士|推荐练习题(C++)

news2024/9/25 15:26:07
线性DP,0/1背包简化版,装箱问题

【题目描述】有一个箱子容量为V(正整数, 0 ≤ V ≤ 20000 0 \le V \le 20000 0V20000),同时有n个物品( 0 < n ≤ 30 0 < n \le 30 0<n30),每个物品有一个体积(正整数)。要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
【输入描述】输入第一行,一个整数,表示箱子容量。
第二行,一个整数n,表示有n个物品。
接下来n行,分别表示这n个物品的各自体积。
【输出描述】输出一行,表示箱子剩余空间。
0/1背包的简化版,不管物品的价格。把体积(不是价格)看成最优化目标:最大化体积。

#include<bits/stdc++.h>
using namespace std;
int dp[20010];
int w[40];
int main()
{
	int V,n;
	scanf("%d%d" ,&V,&n);
	for(int i = 1; i <= n; i ++)
		scanf("%d",&w[i]);
	for(int i = 1; i <= n; i ++)
		for(int j = V; j >= w[i]; j --)
			dp[j] = max(dp[j], dp[j-w[i]]+w[i])
	printf("%d\n", V-dp[V]);
	return 0;
}
计数DP,0/1背包的方案数

【题目描述】
将2022拆分成10个互不相同的正整数之和,总共有多少种拆分方法?
注意交换顺序视为同一种方法。
例如:
2022 = 1000+1022
2022 =1022+1000
视为同一种方法。

题目求10个数的组合情况,这十个数相加等于2022。因为是填空题可以不管运行时间,看起来可以用暴力for循环10次,加上剪枝。
然而暴力的时间极长,因为答案是:379187662194355221

这一题其实是0/1背包:
背包容量为2022,物品体积为1~2022,往背包中装10个物品,要求总体积为2022,问一共有多少种方案。

定义dp[][][]: dp[i][j][k]表示数字1~i取j个,和为k的方案数。下面的分析沿用标准0/1背包的分析方法,
从i-1扩展到i,分两种情况:

  1. k ≥ i k \ge i ki。数i可以要,也可以不要
    1. 要i。从1~i-1中取j-1个数,再取i,等价于dp[i-1][j-1][k-i]
    2. 不要i。从1~i-1中取j个数,等价于dp[i-1][j][k]
    3. 合起来:dp[i][j][k]= dp[i-1][j][k]+ dp[i-1][j-1][K-i]
  2. k < i.
    由于数i比总和k还术、显然i不能用。
    有:dp[i][j][k]= dp[i-1][j][k]
for (int i = 0; i <= 2022; i ++)
	dp[i][0][0] = 1;   //特别注意这个初始化

1~i个数,选0个和为0的情况只有一种,就是不选

(1)k≥i。dp[i][j][k]= dp[i-1][j][k]+ dp[i-1][j-1][k-i]
(2)k<i。dp[i][j][k]= dp[i-1][j][k]

不用滚动数组
#include <bits/stdc++.h>
using namespace std;
long long dp[2222][11][2222]={0};
int main()
{
	for(int i = 0; i <= 2022; i ++)
		dp[i][0][0] = 1;    //特别注意这个初始化
	for(int i = 1; i <= 2022; i ++)
		for(int j = 1; j <= 10; j ++)  //注意:j从小到大,或从大到小都行
			for(int k = 1; k <= 2022; k ++)
			{
				if(k<i)
					dp[i][j][k] = dp[i-1][j][k]; //无法装进背包
				else
					dp[i][j][k] = dp[i-1][j][k] + dp[i-1][j-1][k-i];
	cout << dp[2022][10][2022];
	return 0;
}
使用滚动数组
#include <bits/stdc++.h>
using namespace std;
long long dp[11][2222];
int main()
{
	dp[0][0]=1;
	for(int i = 1; i <= 2022; i ++)
		for(int j = 10; j >= 1; j --) //j一定要从大到小
			for(int k = i; k <= 2022; k ++)
				dp[j][k] += dp[j-1][k-i];
	cout << dp[10][2022];
	return 0;
}
网格上的DP,过河卒

【题目描述】
棋盘上A点有一个过河卒,需要走到目标B点。
卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
现在要求你计算出卒从 A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。棋盘用坐标表示,A点(0,0)、B点(n,m),同样马的位置坐标是需要给出的。1≤n,m≤20,0≤马的坐标≤20。【输入格式】
一行四个正整数,表示B点坐标和马的坐标。
【输出格式】
一个整数,表示所有的路径条数。
![[Pasted image 20240323150439.png]]

统计路径条数,看起来是个搜索题,可以用DFS求解。把马的控制点标记为不能走,绕过它们。
不过,用DFS搜索的路径数量是天文数字,肯定超时
在小学上过奥数的都知道,这题应该用“标数法”,就是在每个坐标点上记录能走的路径条数。
标数法实际上就是DP的递推。

定义状态dp[][]:
dp[i][j]表示卒走到坐标(i,j)时能走的路径条数。
如果不考虑马的控制点,有:dp[i][j]= dp[i-1][j]+ dp[i][j - 1]
也就是(i,j)点的路径条数等于它上面和左边的路径条数之和。这就是小学奥数的“标数法”的原理
本题的限制条件是马的控制点,只要令控制点的dp[i][j] = 0即可,即这个点上无路径

#include <bits/stdc++.h>
using namespace std;
long long dp[25][25];
bool s[25][25];  //标记马的9个控制点
int main()
{
	int bx,by,mx,my;
	scanf("%d%d%d%d",&bx,&by,&mx,&my);
	bx += 2;
	by += 2;
	mx += 2;
	my += 2;
	//技巧:坐标点加2,防止越界,因为马能向上向左跳2格
	dp[2][1] = 1;  	//初始化
	s[mx][my] = 1;	//标记马的控制点
	s[mx - 2][my + 1] = 1;
	s[mx - 2][my - 1] = 1;
	S[mx + 2][my - 1] = 1;
	s[mx + 2][my + 1] = 1;
	s[mx - 1][my - 2] = 1;
	s[mx - 1][my + 2] = 1;
	s[mx + 1][my + 2] = 1;
	s[mx + 1][my - 2] = 1;
	for(int i = 2; i <= bx; i ++)
		for(int j = 2; j <= by; j ++)
		{
			if(s[i][j])
				dp[i][j] = 0;  //这个点是控制点,不能走
			else
				dp[i][j] = dp[i-1][j] + dp[i][j - 1];
		}
	printf("%lld\n", dp[bx][by]);  //结果是个极大的数字
	return 0;
}
完全背包,小明的背包2

【题目描述】
小明有一个容量为C的背包。这天他去商场购物,商场一共有N 种物品,第i种物品的体积为 c i c_{i} ci,价值为 w i w_{i} wi,每种物品都有无限多个。小明想知道在购买的物品总体积不超过C的情况下所能获得的最大价值为多少,请你帮他算算。
【输入描述】
输入第1行包含两个正整数N,C,表示商场物品的数量和小明的背包容量。
第2~N+1行包含2个正整数c,w,表示物品的体积和价值。
1≤N≤103,1≤C≤103,1≤ci,wi≤10^3
【输出描述】
输出一行整数表示小明所能获得的最大价值

思路和0/1背包类似。0/1背包的每种物品只有1件,完全背包的每种物品有无穷多件,第i种可以装0件、1件、2件、C/ c i c_{i} ci件。
定义dp[i][j]:把前i种物品(从第1种到第i种)装入容量为j的背包中获得的最大价值。
把每个dp[i][j]都看成一个背包:背包容量为j,装1~i这些物品。最后得到的dp[N][C]就是问题的答案:把N种物品装进容量c的背包的最大价值。
在0/1背包问题中,每个物品只有拿与不拿两种;而完全背包问题,需要考虑拿几个。

1.定义变量并输入
参考 0-1 背包。
2.执行算法
我们先去找到状态转移方程

dp[i][j]=max(dp[i], dp[i][j-c[i]]+w[i])

选到第i件物品,且背包现在重是为j。
那么考虑这个状态会由什么状态转移而来,肯定是选到第 i-1 件的时候,或者选了若干次第i件
如果不选第i种,那么就是由dp[i-1][i]转移而来
如果选了第i件,那么就是由dp[i-1][j-c[i]]转移而来。
或者在某一刻,不在选第i件了,那么就是有dp[i][j]转移而来。
那么已知dp[i][j-c[i]]dp[i][j]都为各自最优的状态,那我们直接取最优状态即可

#include <iostream>
#include <cstring>

using namespace std;
#define Maxn 5000;

int c[Maxn], w[Maxn];
int dp[Maxn][Maxn];
int C;

int n;

int main()
{
	cin >> n;
	cin >> C;
	for (int i = 1; i <= n; i ++)
		cin >> c[i] >> w[i];
	memset(dp, 0, sizeof(dp));

	for (int i = 1; i <= n; i ++)
	{
		for (int j = 1; j <= C; j ++)
		{
			dp[i][j] = dp[i - 1][j];
			if (j >= c[i])
				dp[i][j] = max(dp[i][j], dp[i][j-c[i]] + w[i]);
		}
	}
	cout << dp[n][C] << endl;
}

因为状态转移每次只与上一层有关,所以用一个一维数组就可以。
为什么从小到大遍历,看

dp[j]=dp[j-c[i]]+w[i]

这一状态转移,是根据小的改大的,而此时的含义为选了x件后的容量与质量,跟01背包类似,但含义不同,处理方式上也有本质区别,处理完一件后在处理下件。

#include <iostream>
#include <cstring>

using namespace std;
#define Maxn 5000;

int c[Maxn], w[Maxn];
int dp[Maxn];
int C;

int n;

int main()
{
	cin >> n;
	cin >> C;
	for (int i = 1; i <= n; i ++)
		cin >> c[i] >> w[i];
		
	memset(dp, 0, sizeof(dp));

	for (int i = 0; i < n; i ++) //遍历每一件物品
	{
		//遍历背包容量,在上一层的基础上,容量为j时,第i件物品装或不装的最优解
		for (int j = c[i]; j <= C; j ++)
		{
			dp[j] = max(dp[j-c[i]] + w[i], dp[j]);
		}
	}
	cout << dp[C] << endl;
}
线性DP,最长公共子序列

【题目描述】
给定一个长度为n的数组A和一个长度为m的数组B。请你求出它们的最长公共子序列长度为多少
【输入描述】
输入第一行包含两个整数n、m。
第二行包含n个整数ai,
第三行包含m个整数bi,
1≤n,m≤103,1≤ai,bi≤109
【输出描述】
输出一行整数表示答案。

暴力法:先找出A的所有子序列,然后一一验证是否为Y的子序列。
如果A有m个元素,那么A有 2 m 2^{m} 2m个子序列;B有n个元素;总复杂度大于 O ( n ∗ 2 m ) O(n*2^m) O(n2m).

dp[i][j]:房列Ai(a1ai)和Bj(b1bj)的最长公共子序列的长度。
答案:dp[n][m]
分解为2种情况

  1. a i = b j a_{i}=b_{j} ai=bj;时已求得 A i − 1 A_{i-1} Ai1 B j − 1 B_{j-1} Bj1的最长公共子序列,在其尾部加上 a i a_{i} ai b j b_{j} bj即可得到Ai和Bj的最长公共子序列。 状态转移方程:
    dp[i][j]=dp[i-1][j-1]+ 1
  2. a i ≠ b j a_{i}\ne b_{j} ai=bj时,求解两个子问题: A i − 1 A_{i-1} Ai1 B j B_{j} Bj的最长公共子序列; A i A_{i} Ai B j − 1 B_{j-1} Bj1的最长公共子序列。取最大值,状态转移方程:
    dp[i][j]= max(dp[i][j-1], dp[i-1][j])
#include <iostream>
#include <cstring>
using namespace std:
#define Maxn 5000

int dp[Maxnl[Maxn];
//DP 辅助数组
int a[Maxn], b[Maxn];

int main()
{
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < n; i ++)
		cin >> a[i];
	for(int i = 0; i < m; i ++)
		cin >> b[i];
	
	dp[0][0] = 0;
	
	for (int i = 0; i < n; i ++)
		for (int j = 0; j < m; j ++)
			if (a[i] == b[j])
				dp[i + 1][j + 1] = dp[i][j] + 1;
			else
				dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);
	cout << dp[n][m]<< endl;
}
最长递增子序列,蓝桥骑士

【题目描述】
小明是蓝桥王国的骑士,他喜欢不断突破自我。这天蓝桥国王给他安排了N个对手,他们的战力值分别为 a 1 , a 2 , . … ,, a n a_{1},a_{2},.…,,a_{n} a1a2.,,an,且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战,也可以选择避战。身为高傲的骑士,小明从不走回头路,且只愿意挑战战力值越来越高的对手。
请你算算小明最多会挑战多少名对手。
【输入描述】
第一行是整数N,表示对手的个数,第二行是N个整数 a 1 , a 2 , . … ,, a n a_{1},a_{2},.…,,a_{n} a1a2.,,an,表示对手战力值。1≤N≤3x10^5
【输出描述】
输出一行整数表示答案。

求一个子序列,保证这个子序列时递增的,最长是多少

给定一个长度为n的数组,找出一个最长的单调递增子序列。
例:序列A={5,6,7,4,2,8,3},它最长的单调递增子序列为{5,6,7,8},长度为4。
定义状态dp[i]:表示以第i个数为结尾的最长递增子序列的长度。
状态转移方程:
dp[i]= max{dp[j]}+1, 0<j<i, A < Ai
答案:max{dp[i]}
复杂度:
j在0~i之间滑动,复杂度O(n);
i的变动范围也是O(n)的;
总复杂度O(n2)

我们首先定义状态:
我们定义dp[i]为以a[i]结尾的最长上升子序列长度。
设置j为小于i的某一点,那么当 a[j]<a[i] 时,必然有,以 a[j] 结尾的最长上升子序列,现在能以 a[i] 结尾,并且长度 +1。
因为 j<i且 a[j]< a[i],满足上升子序列的要求。
所以 dp[i] 的一条转移路径为 dp[i]=dp[j]+1
但是j是比i小的某一个值,所以转移到 dp[i] 这一状态的值很多,我们要选择最优状态,所以 dp[i]的状态转移:
dp[i]=max(dp[j]+1, dp[i]);
那么当 a[j]>a[i]
此时肯定不满足上升子序列,所以 dp[i]dp[j] 毫无关联。由此我们可以写出 LIS 的算法部分

#include <bits/stdc++.h>

using namespace std;
//求最长升序子序列的长度,并输出
#define Maxn 300006
int a[Maxn];
int dp[Maxn];
int ans=1:
int main()
{
	int m, k;
	int n;
	
	cin >> n;
	for(int i = 0; i < n; i ++)
	{
		cin >> a[i];
		dp[i] = 1;
	}
	for(int i = 1; i < n; i ++)
	{
		for(int j = 0; j < i; j ++)
		{
			if(a[j] < a[i])
			{
				dp[i] = max(dp[j] + 1, dp[i]);
			}
			ans=max(ans, dp[i]);
		}
	}
	cout << ans << endl;:
}

练习题
  1. 李白打酒加强版2114
  2. 分果果1459
  3. 开心的金明554
  4. 最优包含239
  5. 质数行者1027
  6. 货币系统331
  7. 合唱队形742
  8. 数组切分2148
  9. 括号序列1456
  10. 传球游戏525
  11. 画廊1032
  12. 游园安排1024
  13. 凑硬币1082
  14. 纪念品786。
  15. 积木画2110
  16. 采药563
  17. 摆花389;
  18. 蓝肽子序列1030
  19. 矩阵计数246
  20. 方格取数803

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1539245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu20.04安裝輸入法

文章目录 前言一、操作過程1、安装fcitx-googlepinyin2、配置language support 前言 參考文獻 一、操作過程 1、安装fcitx-googlepinyin sudo apt-get install fcitx-googlepinyin2、配置language support 第一次點擊進去&#xff0c;會讓你安裝 點擊ctrl和空格切換中英文…

Gin入门指南:从零开始快速掌握Go Web框架Gin

官网:https://gin-gonic.com/ GitHub:https://github.com/gin-gonic 了解 Gin Gin 是一个使用 Go 语言开发的 Web 框架,它非常轻量级且具有高性能。Gin 提供了快速构建 Web 应用程序所需的基本功能和丰富的中间件支持。 以下是 Gin 框架的一些特点和功能: 快速而高效:…

000_coolprop_in_matlab在Matlab中使用CoolProp

在Matlab中使用CoolProp 简介 CoolProp是一个开源的热力学性质库&#xff0c;可以计算多种流体的热力学性质。CoolProp支持多种编程语言&#xff0c;包括Python、C、Matlab等。本文将介绍如何在Matlab中使用CoolProp。 CoolProp官网 本文所使用的Matlab版本为R2021a。 在Ma…

Zabbix与Prometheus区别简述

Zabbix与Prometheus区别简述 历史沿革 一、监控工具简介 1、Zabbix https://www.zabbix.com/cn/download Zabbix是传统的监控系统&#xff0c;出现比云原生早&#xff0c;使用的是SQL关系型数据库&#xff1b;开源监控软件&#xff0c;遵守 GPLv2开源协议&#xff0c;起源于…

为什么说 C/C++ 不适合做 Web 开发?

Web开发是当今信息技术领域的重要组成部分&#xff0c;涉及创建、维护和优化供浏览器访问的网站和应用程序。在众多编程语言中&#xff0c;C/C因其高效、灵活和接近硬件的特性而备受推崇&#xff0c;但在Web开发领域&#xff0c;它们并不被视为首选工具。本文将从以下几个方面探…

Linux Sftp和Scp

scp 和 sftp 区别 1 scp 能将远程文件复制到另一个远程机&#xff0c;sftp 不能。sftp为 SSH的其中一部分&#xff0c;是一种传输档案至 Blogger 伺服器的安全方式 2.scp 没有删除/创建远程目录功能&#xff0c;sftp 有。scp 在需要进行验证时会要求你输入密码或口令。 3. FT…

【算法】回溯与深搜

方法论 1.构建决策树 2.设计代码&#xff1a;全局变量、dfs函数 3.剪枝&#xff0c;回溯 全排列 给定一个不含重复数字的整数数组 nums &#xff0c;返回其 所有可能的全排列 。可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff…

力扣---随机链表的复制

给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节点的 n…

进程和线程的关系与区别

目录&#xff1a; 导言&#xff1a; 正文&#xff1a; 一.进程 1.概念 2.进程控制块&#xff08;Process Control Block&#xff0c;PCB) 3.状态 4.优缺点 二.线程 1.概念 2.线程的设计理由与优点 3.缺点 三.进程与线程的区别与联系 导言&#xff1a; 在现代计算机…

ctf__

title: ctf 2.题目类别 3.题目的细分 4.例题 misc 0x05. 明文攻击 明文攻击是一种较为高效的攻击手段&#xff0c;大致原理是当你不知道一个zip的密码&#xff0c;但是你有zip中的一个已知文件&#xff08;文件大小要大于12Byte&#xff09;时&#xff0c;因为同一个zip压缩…

java数据结构与算法刷题-----LeetCode215. 数组中的第K个最大元素

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路&#xff1a;时间复杂度O( n n n)&#xff0c;空间复杂度…

大屏可视化综合展示解决方案

1.系统概述 1.1.需求分析 1.2.重难点分析 1.3.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 2.3.接口及要求 3.系统功能设计 3.1.功能清单列表 3.2.数据源管理 3.3.数据集管理 3.4.视图管理 3.5.仪表盘管理 3.6.移动端设计 3.1.系统权限设计 3.…

STM32 CAN的位时序和波特率

STM32 CAN的位时序和波特率 位时序 STM32的CAN外设位时序中只包含3段&#xff0c;分别是同步段SYNC_SEG、位段BS1及位段BS2&#xff0c;采样点位于BS1及BS2段的交界处。其中SYNC_SEG段固定长度为1Tq&#xff0c;而BS1及BS2段可以在位时序寄存器CAN_BTR设置它们的时间长度&…

CISP 4.2备考之《安全支撑技术》知识点总结

文章目录 第一节 密码技术第二节 标识和身份鉴别技术第三节 访问控制技术 第一节 密码技术 密码学发展阶段&#xff1a;古典、近代、现代和公钥密码学及特点。 密码系统组成&#xff1a;明文、加密、密钥、解密、密文。 柯克霍夫原则&#xff1a;密钥保密&#xff0c;算法公开…

Stable Diffusion 本地训练端口与云端训练端口冲突解决办法

方法之一&#xff0c;修改本地训练所用的端口 1 首先&#xff0c;进入脚本训练器的根目录 例如&#xff1a;C:\MarkDeng\lora-scripts-v1.7.3 找到gui.py 2 修改端口号 因为云端训练器也是占用28000和6006端口 那么本地改成27999和6007也是可以的 保存退出&#xff0c;运行启动…

NX二次开发常用函数:UF_MODL_ask_feat_......(一)

最近学习NX二次开发发现有一些函数经常使用&#xff0c;俗话说得好&#xff0c;好记性不如烂笔头&#xff0c;现在做一下笔记&#xff0c;帮助理解。 UF_MODL_ask_feat_......所在头文件uf_modl.h 1、UF_MODL_ask_feat_body&#xff08;通过特征查询体&#xff09; 概述&…

liunx centos7 下通过yum删除安装已经安装的php

执行下面命令查看php相关的包 rpm -qa | grep php 只需要卸载几个名为common的包即可&#xff0c;其他同版本依赖会被全部删除&#xff0c;删除php71w-common&#xff0c;71w版本的依赖包全部会被删除。 查看php包的命令 rpm -qa | grep php 或 yum list installed | gre…

鸿蒙APP应用开发教程—超详细的项目结构说明

1. 新建项目 打开DevEco Studio, 选择 Create Project: 1.1 选择模版 Create Project - Choose Template 1.2 配置项目 Create Project - Configure Project 如果使用的是 DevEco 3.X 版本, 可以根据 Compile SDK版本选择不同的模式, 比如: 3.0.0(API 8)及更早 - 仅支持 …

Docker-Image

Docker Docker 镜像是什么为什么需要镜像镜像命令总览docker imagesdocker tagdocker pulldocker pushdocker rmidocker savedocker loaddocker image inspectdocker historydocker importdocker image prunedocker build Docker 镜像是什么 Docker image 本质上是一个 read-on…

图像分类从零开始(1)

尽我所能&#xff0c;总结留给后面的师弟们&#xff01; 1.目标 搭建一个完整的系统&#xff0c;包括图像数据集预处理&#xff0c;训练模型&#xff0c;分类器&#xff0c;优化器&#xff0c;以及结果数据处理。 2.理论 3.实例&#xff08;猫狗分类&#xff09; Gitee代码…