动态规划(3):熟练度练习(POJ 1458、最佳加法表达式、bailian2755、POJ3624、bailian1088)

本文主要是介绍动态规划(3):熟练度练习(POJ 1458、最佳加法表达式、bailian2755、POJ3624、bailian1088),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最长公共子序列

Language:Default
Common Subsequence

Time Limit: 1000MSMemory Limit: 10000K
Total Submissions: 47599Accepted: 19562

Description

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, x ij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input

The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output

For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input

abcfbc         abfcab 
programming contest
abcd mnp

Sample Output

4 
2
0

Source

Southeastern Europe 2003
线性DP基础模型 (下面会着重讲线性DP和区间DP 具体是什么到时候会讲)

输入两个串s1,s2,

设MaxLen(i,j)表示:
s1的左边i个字符形成的子串,与s2左边的j个字符形成的子串的最长公共子序列的长度(i,j从0开始算)
MaxLen(i,j) 就是本题的“状态”

假定 len1 = strlen(s1),len2 = strlen(s2)
那么题目就是要求 MaxLen(len1,len2)

套用基本思路中的定义, 我们来大致说明一下上面子问题的正确定(无严格证明)

首先我们要求字符串s1(长度为len1), s2(长度为len2)的最长公共子序列, 也就是求(len1, len2)个字符的最长公共子串。我们将问题拆分成求(i, j)个字符的最长公共子串。

正如前文说的, 他需要满足三个条件这个拆分才是成立的:具有相同的子问题*满足最优子结构*无后效性

(1)具有相同子问题:(len1, len2)可以转化为(len1 - 1, len2)和(len1, len2 - 1), 这样依次划分下去最终的问题划分结果就是(0, 0)这显然是一个可以解决的最终分割情况。

(2)满足最优子结构:我们已知(0, 0)的值是0, 这一定是最优的结构, 那么我们设(i-1, j)、(i, j-1)和(i-1, j-1)都是最优子结构, 对于(i,j)的值, 我们可以通过查看(str1[i], str2[j])的关系得来, 与之前的(i-1,j-1)无关

(3) 无后效性:每一步我们只考虑各自的(i,j)个节点, 对于后面节点的选择与前面节点的选择方式无关(这里的影响是指:在求(i,j+2)的时候与(i,j+1)以及之前的节点的选择方式无关)

(4)此题特别需要证明的一点:S1[i-1]!= s2[j-1]时,MaxLen(S1,S2)不会比MaxLen(S1,S2j-1)和MaxLen(S1i-1,S2)两者之中任何一个小,也不会比两者都大。

递推公式

if ( s1[i-1] == s2[j-1] ) //s1的最左边字符是s1[0]MaxLen(i,j) = MaxLen(i-1,j-1) + 1;//这里的证明暂时不证
elseMaxLen(i,j) = Max(MaxLen(i,j-1),MaxLen(i-1,j) );

边界情况

MaxLen(n,0) = 0 ( n= 0…len1)
MaxLen(0,n) = 0 ( n=0…len2)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define maxn 1010char str1[maxn];
char str2[maxn];
int maxlen[maxn][maxn];using namespace std;int main(void)
{while (cin >> str1 >> str2){int length1 = strlen(str1);int length2 = strlen(str2);int ntemp;int i, j;for (i = 1; i <= length1; i++)maxlen[i][0] = 0;for (j = 0; j <= length2; j++)maxlen[0][j] = 0;for (int i = 1; i <= length1; i++){for (int j = 1; j <= length2; j++){if (str1[i - 1] == str2[j - 1])maxlen[i][j] = maxlen[i - 1][j - 1] + 1;elsemaxlen[i][j] = max(maxlen[i - 1][j], maxlen[i][j - 1]);}}printf("%d\n", maxlen[length1][length2]);}return 0;
}

最佳加法表达式

假定数字串长度是n,添完加号后,表达式的最后一个加号添加在第 i 个数字后面,那么整个表达式的最小值,就等于在前 i 个数字中插入 m – 1个加号所能形成的最小值,加上第 i + 1到第 n 个数字所组成的数的值(i从1开始算)。

对于这样的一道题, 我们用如下的二元状态来形容(n,m)代表在n个数中插入m个加号(这满足前三种要求, 可以自己证明一下, 其实在证明的时候只需证明“无后效性”)


我们从后往前看, 假设我们在第i,j之间放置加号 那么(n,m) = (i,m - 1) + num(j, n);翻译一下就是[j,n]形成的数 + 前i个数中插入m-1个加号形成的最大值。(这的(n,m)仅仅是用来说明递归关系, 并不一定是正确值, 完整方程在下面)

if m = 0V(n,m) = n个数字构成的整数
else if n < m + 1V(n,m) = ∞
elseV(n,m) = Min{ V(i,m-1) + Num(i+1,n) } ( i = m … n-1);

递归代码(可以自己尝试一下递推, 只要将这个递归回溯的过程单独拿出来, 就能构造成我为人人型递归)

#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;long long num[110][110];
int number[100];
long long mark[100][100];long long V(int n, int m)//n个数  m个加号
{//printf("%d %d\n", n, m);if (m == 0)return num[1][n];else if (n == 0)return 0xfffffff;else{if (mark[n][m] != 0)return mark[n][m];long long fin = 0xfffffff;for (int i = m; i <= n - 1; i++)fin = min(fin, V(i, m - 1) + num[i + 1][n]);mark[n][m] = fin;return fin;}
}int main(void)
{int n, m;while (scanf("%d %d", &n, &m) != EOF)//n个数, m个加号{for (int i = 1; i <= n; i++)scanf("%d", &number[i]);for (int i = 1; i <= n; i++){num[i][i] = number[i];for (int j = i + 1; j <= n; j++){num[i][j] = num[i][j - 1] * 10 + number[j];//printf("%d %d %lld\n", i, j, num[i][j]);}}memset(mark, 0, sizeof(mark));printf("%lld\n", V(n, m));}return 0;
}

bailian2755

总时间限制: 10000ms 内存限制: 65536kB
描述
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。

输入
输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。

输出
输出不同的选择物品的方式的数目。

样例输入
3
20
20
20

样例输出
3

这里基本可以看作是一种套路, 属于背包的套路。背包的基本子问题划分方式就是对于第i项物品选还是不选。在背包问题中选与不选就导致了两种状态之间不同的转移方式(1)dp[k] = max(dp[k], dp[k-v[i]] + s[i]);

这里max中的dp[k]对应的就是不选择第i项物品的情况, v[k]就是付出的价值s[i]就是因为选择这个物品而得到的价值。所以dp[k - v[i]] + s[i]就对应着选择地i种物品的情况。然后再这两者之间求最大值就是背包问题的基本思路。

在这里是对标准背包问题的一个小小的变形, 在这里我们考虑的是最终装满背包的总方案数, 这里在求dp[i]中的max函数也就应该换乘‘+’(因为在这里并不是在求多个路径中最优的那一个, 而是不同路径的个数, 自然应该在不同节点统计不同的路径数)

状态转移方程

dp[i] = sum(dp[i - v[K]]), ( K = 1,2,3 …… n) && (i > v[K])

#include <iostream> 
#define MAX 41
using namespace std;int main()
{ int n,i,j,input; int sum[MAX];memset(sum, 0, sizeof(sum));cin >> n; sum[0] = 1;for(i=0;i<n;i++){ cin >> input; for (j = 40; j >= 1; j--){if(j - input >= 0)sum[j] += sum[j - input];}} cout << sum[40] << endl; return 0;
}

POJ 3624

Language:Default
Charm Bracelet
Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 33379Accepted: 14790

Description

Bessie has gone to the mall’s jewelry store and spies a charm bracelet. Of course, she’d like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a ‘desirability’ factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).

Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.

Input

* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di

Output

* Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints

Sample Input

4 6
1 4
2 6
3 12
2 7

Sample Output

23

Source

USACO 2007 December Silver

分析

标准的01背包递推: F[i][j] = max(F[i-1][j],F[i-1][j-w[i]]+d[i])边界:if (w[1] <= j)F[1][j] = d[1];elseF[1][j] = 0;

注意这道题唯一的坑点就在于数据量过大导致没有足够的空间来存放二维数组, 注意到这个二维数组的下一行的值,只用到了上一行的正上方及左边的值,因此可用滚动数组的思想,只要一行即可。即可以用一维数组,用“人人为我”递推型动归实现。

#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;int dp[13010];
int w[3510], d[3510];int main(void)
{int n, m;while (scanf("%d %d", &n, &m) != EOF){for (int i = 1; i <= n; i++){scanf("%d %d", &w[i], &d[i]);}memset(dp, 0, sizeof(dp));for (int i = 1; i <= n; i++){for (int j = m; j >= w[i]; j--){dp[j] = max(dp[j], dp[j - w[i]] + d[i]);}}printf("%d\n", dp[m]);}return 0;
}

·
·
·

滑雪

总时间限制: 1000ms 内存限制: 65536kB
描述
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
输入
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
输出
输出最长区域的长度。
样例输入
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出
25

递推公式

L(x, y) = max(L(x - 1, y), L(x + 1, y), L(x, y - 1), L(x, y + 1)) + 1;

分析:

1) “人人为我”式递推

L(i,j)表示从点(i,j)出发的最长滑行长度。 一个点(i,j), 如果周围没有比它低的点,L(i,j) = 1
将所有点按高度从小到大排序。每个点的 L 值都初始化为1 从小到大遍历所有的点。经过一个点(i,j)时,用递推公式求L(i,j)

2)“我为人人”式递推

L(i,j)表示从点(i,j)出发的最长滑行长度。 一个点(i,j), 如果周围没有比它低的点,L(i,j) = 1
将所有点按高度从小到大排序。每个点的 L 值都初始化为1 从小到大遍历所有的点。经过一个点(i,j)时,要更新他周围的,比它高的点的L值。

代码(我为人人形式)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>using namespace std;struct node {int x;int y;int heigh;node(int a = 0, int b = 0, int c = 0) :x(a), y(b), heigh(c) {}friend bool operator < (node a, node b){return a.heigh > b.heigh;}
}map[110][110];
int n, m;
int dir[4][2] = { {-1,0}, {1,0}, {0,1}, {0,-1} };
int dp[110][110];int main(void)
{while(scanf("%d %d", &n, &m) != EOF){priority_queue<node>Q;int maxx = 1;for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){scanf("%d", &map[i][j].heigh);map[i][j].x = i;map[i][j].y = j;Q.push(node(i, j, map[i][j].heigh));dp[i][j] = 1;}}while (!Q.empty()){node a = Q.top();Q.pop();for (int i = 0; i < 4; i++){int xx = a.x + dir[i][0];int yy = a.y + dir[i][1];if (xx <= 0 || xx > n || yy <= 0 || yy > m)continue;if (map[xx][yy].heigh <= map[a.x][a.y].heigh)continue;dp[xx][yy] = max(dp[xx][yy], dp[a.x][a.y] + 1);maxx = max(maxx, dp[xx][yy]);}}printf("%d\n", maxx);}return 0;
}

这篇关于动态规划(3):熟练度练习(POJ 1458、最佳加法表达式、bailian2755、POJ3624、bailian1088)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/708705

相关文章

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

SpringBoot项目中Maven剔除无用Jar引用的最佳实践

《SpringBoot项目中Maven剔除无用Jar引用的最佳实践》在SpringBoot项目开发中,Maven是最常用的构建工具之一,通过Maven,我们可以轻松地管理项目所需的依赖,而,... 目录1、引言2、Maven 依赖管理的基础概念2.1 什么是 Maven 依赖2.2 Maven 的依赖传递机

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情