动态规划(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

相关文章

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

SpringBoot实现Kafka动态反序列化的完整代码

《SpringBoot实现Kafka动态反序列化的完整代码》在分布式系统中,Kafka作为高吞吐量的消息队列,常常需要处理来自不同主题(Topic)的异构数据,不同的业务场景可能要求对同一消费者组内的... 目录引言一、问题背景1.1 动态反序列化的需求1.2 常见问题二、动态反序列化的核心方案2.1 ht

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

Vue 2 项目中配置 Tailwind CSS 和 Font Awesome 的最佳实践举例

《Vue2项目中配置TailwindCSS和FontAwesome的最佳实践举例》:本文主要介绍Vue2项目中配置TailwindCSS和FontAwesome的最... 目录vue 2 项目中配置 Tailwind css 和 Font Awesome 的最佳实践一、Tailwind CSS 配置1. 安

Python Selenium动态渲染页面和抓取的使用指南

《PythonSelenium动态渲染页面和抓取的使用指南》在Web数据采集领域,动态渲染页面已成为现代网站的主流形式,本文将从技术原理,环境配置,核心功能系统讲解Selenium在Python动态... 目录一、Selenium技术架构解析二、环境搭建与基础配置1. 组件安装2. 驱动配置3. 基础操作模

Spring Boot 常用注解详解与使用最佳实践建议

《SpringBoot常用注解详解与使用最佳实践建议》:本文主要介绍SpringBoot常用注解详解与使用最佳实践建议,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、核心启动注解1. @SpringBootApplication2. @EnableAutoConfi

慢sql提前分析预警和动态sql替换-Mybatis-SQL

《慢sql提前分析预警和动态sql替换-Mybatis-SQL》为防止慢SQL问题而开发的MyBatis组件,该组件能够在开发、测试阶段自动分析SQL语句,并在出现慢SQL问题时通过Ducc配置实现动... 目录背景解决思路开源方案调研设计方案详细设计使用方法1、引入依赖jar包2、配置组件XML3、核心配

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾