着手SDUT OJ提高实验—动态规划,之前对动态规划的复习#Round1

来源:互联网 发布:Ubuntu 安装c 编译器 编辑:程序博客网 时间:2024/06/05 19:21

/**我们在#Round1里主要复习动态规划的简单例题,回忆动态规划最原始的思路,重新理解“状态”和“最优子结构”以及“后效性”这三个重要概念。

例1 过河卒 SDUT OJ 1265,洛谷OJ 1002

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

输入输出格式

输入格式:
一行四个数据,分别表示B点坐标和马的坐标。

输出格式:
一个数据,表示所有的路径条数。

☆类型:棋盘型DP
☆注意问题:将矩阵初值都设置为false,读入数据时,将可行棋盘设置为true,防止越界。
☆状态设置:当前点的路径条数dp[i,j](博客格式问题,这里使用Pascal数组表示方法,都能看懂哈)是上一步向右,或者上一步向上的路径条数之和,即:F[i,j]=F[i-1,j]+F[i,j-1]。卒所在初值点的坐标F[0,0]=1。
☆核心代码:

for (i=0;i<=n;i++)    for (j=0;j<=m;j++)        if (pd[i,j]) F[i,j]=F[i-1,j]+F[i,j-1];

例2 方格取数 洛谷OJ 1004,传纸条 洛谷OJ 1006

题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。

输入输出格式

输入格式:
输入文件message.in的第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。
接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。

输出格式:
输出文件message.out共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。

☆类型:双线程棋盘型DP
☆注意问题:从A到B需要走两次,使取数之和最大。
☆状态设置:dp[i,j,k,p],[i,j]为来时状态,[k,p]为去时状态。同上一个例题当前dp[i,j,k,p]即来价格总和最大为来时上一步和回去时上一步价格最大累加和(注意这里只去一个最大值,并不是都加起来)给出方程:
dp[i,j,k,p]=max{dp[i-1,j,k-1,p],dp[i-1,j,k,p-1],dp[i,j-1,k-1,p],dp[i,j-1,k,p-1]}+a[i,j];
if (i!=k&&j!=p) dp[i,j,k,p]+=a[k,p];(由于来回只能取同一个方格的一次值,所以这里要判断一下是否经过同一个点)
☆核心代码:

for (i=1;i<=n;i++)    for (j=1;j<=n;j++)        for (k=1;k<=n;k++)            for (p=1;p<=n;p++)            {                dp[i,j,k,p]=max{dp[i-1,j,k-1,p],dp[i-1,j,k,p-1],                                dp[i,j-1,k-1,p],dp[i,j-1,k,p-1]}+a[i,j];                if (i!=k&&j!=p) dp[i,j,k,p]+=a[k,p];            }

PS:担心四维的数组会爆内存?自己用滚动数组优化吧!(逃)

例3 数字三角形 Codevs 1220 (洛谷OJ的数字三角形是数论):

题目描述 Description
如图所示的数字三角形,从顶部出发,在每一结点可以选择向左走或得向右走,一直走到底层,要求找出一条路径,使路径上的值最大。

输入描述 Input Description
第一行是数塔层数N(1<=N<=100)。
第二行起,按数塔图形,有一个或多个的整数,表示该层节点的值,共有N行。

输出描述 Output Description
输出最大值。

☆思想:棋盘型DP,记忆化搜索,树形DP
☆注意问题:可以模仿树形DP来进行遍历,亦或直接记忆化搜索
☆状态设置:点i处为父亲节点,点i的最大值为其左下,右下两个子节点最大值加上其本身的值,方程给出:dp[i,j]=max{dp[i+1,j],dp[i+1,j+1]}+s[i,j];
☆核心代码:
【代码1——动态规划做法】

for (i=n-1;i>=1;i--)    for (j=1;j<=i;j++)        dp[i,j]=max(dp[i+1,j],dp[i+1,j+1])+s[i,j]

【代码2——记忆化搜索做法(或者你说是树P也可以)】

void search(int i,int j)//x,y是坐标{    if (x==n)//树底最大权值是自己    {        f[x,y]=a[x,y];        break;    }    if (f[x,y]>1000000) break;//判断是不是搜过,如果搜过就不搜了    search (x+1,y);//分别向左子树和右子树搜索    search (x+1,y+1);    f[x,y]=max(f[x+1,y],f[x+1,y+1])+a[x,y];//状态转移方程}

——————————接下来的三个例题提高了难度,读者做好心理准备——————————

例4 合唱队形 洛谷OJ 1091

题目描述
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<…Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入输出格式

输入格式:
输入文件chorus.in的第一行是一个整数N(2<=N<=100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。

输出格式:
输出文件chorus.out包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

☆思想:序列型DP,最长严格不上升子序列(这个问题的延伸比如子串以及优化时间复杂度会在后续的博客中详细讲解)
☆注意问题:正着DP一遍,倒着DP一遍(也就是说要开两个DP数组),最后for循环两个DP数组相加之和的最大值,输出时要加一(因为有个同学被减了两次)
☆状态设置:dp[i]表示到第i个同学的最长严格不上升子序列的长度,所以有:

if (a[j]<a[i]&&dp[j]>dp[i]) dp[i]=dp[j]//此时的dp[i]还是前一个状态    dp[i]++;//此时的dp[j]的状态就转移到i的状态上来了

可能不是很好理解,下面我给出核心代码
☆核心代码:

for (i=1;i<=n;i++){    for (j=1;j<=i-1;j++)        if (a[j]<a[i]&&dp[j]>dp[i]) dp[i]=dp[j];    dp[i]++;}//如果前一个j小于i并且dp[j]的值大于dp[i]的值,那么更新dp[i],状态转移

例5 乘积最大 洛谷OJ 1018

题目描述
今年是国际数学联盟确定的“2000――世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目
设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
1) 3*12=36
2) 31*2=62
这时,符合题目要求的结果是:31*2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入输出格式

输入格式:
程序的输入共有两行:
第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)
第二行是一个长度为N的数字串。

输出格式:
结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。

☆思想:区间型DP,划分型DP
☆注意问题:转换为数字的代码(同时也为DP的初始化做准备)

for (i=1;i<=n;i++)    for (j=1;j<=n;j++)        a[i,j]=a[i,j-1]*10+(s[j]-'0');

☆状态设置:枚举乘号的位置,dp[i,j]表示第i个数中插入了j个乘号的最大值
☆核心代码:

for (j=1;j<=m;j++)//枚举每个乘号    for (i=1;i<=n;i++)//枚举每个数        for (k=j;k<=i-1;k++)//前i个数的第j个乘号放在第k个位置            f[i,j]=max(f[k,j-1]*a[k+1,j],f[i,j])//取前i个放了j个乘号的值与从j到i-1个位置乘后缀                                                //a[k+1,i](这里的k一定要+1)的最大值

例6 路面修整 Codevs 2572

题目描述 Description
Mr. Ling打算好好修一下学校门口的那条凹凸不平的路。按照Mr. Ling的设想,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中。
整条路被分成了N段,N个整数A_1,…,A_N依次描述了每一段路的高度。Mr.Ling希望找到一个恰好含N个元素的不上升或不下降序列B_1,…,B_N,作为修过的路中每个路段的高度。由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为:
|A_1 - B_1| + |A_2 - B_2| + … + |A_N - B_N|
请你计算一下,Mr. Ling在这项工程上的最小支出是多少。Mr. Ling向你保证,这个支出不会超过2^31-1。

输入描述 Input Description
第1行:输入1个整数N;

第2..N+1行:第i+1行为i个整数A_i

输出描述 Output Description
第1行:输出1个正整数,表示把路修成高度不上升或高度不下降的最小花费。

☆思想:序列型DP
☆注意问题:倒着DP的那一遍要改方程同时a[i]要从大到小排
☆状态设置:我们知道,如果要使填完路的代价最小,就要使读入数组a[i]中的每一个元素都等于有序数组h[i]中的其中一个元素的值,这样才使子问题最优(h[i]是a[i]以升序排列)。dp[i,j]表示数组a[i]中的第i个数与数组h[i]中第j个元素相等时j及i之间所填路的最小代价。
☆核心代码
【代码1正着循环没有优化版本】

for (i=1;i<=n;i++)    for (j=1;j<=n;j++)        for (k=1;k<=j-1:k++)        dp[i,j]=min(dp[k,j-1]+abs(h[i]-a[i]),dp[i,j]); //关于这里的k在此做一个解释: /*在循环的时候,前面被修改的数字到后面有可能有变化的情况,所以它可能取到原数组中的任何一个数字,所以这里要再将k从1遍历到j-1来找原数组中的最小值*/

【代码2正着循环有优化版本】

for (i=1;i<=n;i++)    for (j=1;j<=n;j++)    dp[i,j]=min(dp[i-1,j]+abs([h[i]-a[i]),dp[i,j-1]);/*dp[i-1,j]是i的前状态,dp[i-1,j]+abs(h[i]-a[i])是现状态,dp[i,j-1]是j的前状态.*/

代码2的可行性:从j=1到n,j递增,i一直在向j靠齐,所以正确性显然。

【代码3倒着循环版本】

for (i=1;i<=n;i++) b[i]=a[n-i];for (i=n;i>=1;i--)    for (j=n;j>=1;j--)        dp[i,j]=min(dp[i+1,j]+abs(h[i]-b[i]),dp[i,j+1]); //释疑与上文类同,此处不再阐述

——————————给SDUT OJ 即将入动规坑的同学一点建议————————————

其实呢,个人感觉,SDUT OJ初学动态规划的同学还是建议从背包问题入手,毕竟背包问题是动态规划的经典问题,更是从贪心到动规思维转换的开始。实际上提高实验——贪心,的最后一个问题:装船问题一开始我是想用01背包做的,但是略微读题发现不是那么回事,这可能也是学长设置在贪心之末动规之初的原因吧。所以呢,我的下一期博客会是一个背包问题的专讲,包括01背包,无限背包,有依赖的背包等等,详细的讲解。

溜了溜了~~

原创粉丝点击