acm动态规划总结.docx
- 文档编号:28789229
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:20
- 大小:34.48KB
acm动态规划总结.docx
《acm动态规划总结.docx》由会员分享,可在线阅读,更多相关《acm动态规划总结.docx(20页珍藏版)》请在冰豆网上搜索。
acm动态规划总结
Pkuacm1163theTriangle动态规划题目总结
(一)
题目:
对于一个有数字组成的二叉树,求由叶子到根的一条路径,使数字和最大,如:
7
38
810
2744
45265
这个是经典的动态规划,也是最最基础、最最简单的动态规划,典型的多段图。
思路就是建立一个数组,由下向上动态规划,保存页子节点到当前节点的最大值,Java核心代码如下:
for(inti=num-2;i>=0;i--){
for(intj=0;j<=i;j++){
总结:
这道题是很地道的DP,因为它的子问题实在是太多了,所以将问题的结果保存起来,刘汝佳《算法艺术和信息学竞赛》中115页讲到自底向上的递推,这个例子就非常典型。
总体来说这个题目还是非常简单的,不过这个思想是地道的动态规划。
带有详细注释的代码可以在获得
Pkuacm2081Recaman'sSequence动态规划题目总结(三)
一道很简单的动态规划,根据一个递推公式求一个序列,我选择顺序的求解,即自底向上的递推,一个int数组result根据前面的值依此求出序列的每一个结果,另外一个boolean数组flag[i]记录i是否已经出现在序列中,求result的时候用得着,这样就避免了查找。
核心的java代码为:
for(i=1;i<=500000;i++)
{
if(result[i-1]-i>0&&flag[result[i-1]-i]==false)
{
result[i]=result[i-1]-i;
flag[result[i-1]-i]=true;
}
else
{
result[i]=result[i-1]+i;
flag[result[i-1]+i]=true;
}
}
带有详细注释的代码可以在获得
Pkuacm1953WorldCupNoise动态规划题目总结(四)
给定一个小于45的整数n,求n位2进制数中不含相邻1的数的个数。
看似简单的一道题,如果当n=45时,对2的45次方检查,是无法完成的任务。
先分析一下这个问题:
N
以1结尾的个数
以0结尾的个数
总和
1
1
1
2
2
1
2
3
3
…
…
…
对于n=1来说,以1结尾、以0结尾个数都是1,总和是2,下面过度到2:
对于所有以1结尾的数,后面都可以加上0,变为n=2时以0结尾的,而只有结尾为0的数才能加上1(因为不能有两个连续0),这样就可以在n=2的格里分别填上1、2,总和算出来为3,以此类推,我们可以算出所有n<=45的值,然后根据输入进行相应输出。
核心代码如下:
inti,num,count,array[50][2],j=0;
array[1][1]=1;
array[1][0]=1;
for(i=2;i<50;i++)
{
array[i][0]=array[i-1][1];
array[i][1]=array[i-1][1]+array[i-1][0];
}
我们可以继续找出规律,其实这个就是斐波那切数列数列:
F[N]=F[N-1]+F[N-2];可以继续简化代码。
带有详细注释的代码可以在获得
Pkuacm1458CommonSubsequence动态规划题目总结(五)
求两个string的最大公共字串,动态规划的经典问题。
算法导论有详细的讲解。
下面以题目中的例子来说明算法:
两个string分别为:
abcfbc和abfca。
创建一个二维数组result[][],维数分别是两个字符串长度加一。
我们定义result[i][j]表示Xi和Yj的最长子串(LCS).当i或j等于0时,result[i][j]=0.LCS问题存在一下递归式:
result[i][j]=0i=0orj=0
result[i][j]=result[i-1][j-1]+1Xi==Yj
result[i][j]=MAX(result[i-1][j],result[i][j-1])Xi!
=Yj
对于以上例子,算法如下:
Result[i][j]:
a
b
c
f
b
a
0
1
2
3
4
5
6
0
0
0
0
0
0
0
0
a
1
0
1
1
1
1
1
1
b
2
0
1
2
2
2
2
2
f
3
0
1
2
2
3
3
3
c
4
0
1
2
3
3
3
3
a
5
0
1
2
3
3
3
4
从最后一个格向上顺着箭头的方向可以找到最长子串的构成,在有箭头组成的线段中,含有斜向上的箭头对应的字符是其中的一个lcs。
Java代码的核心部分如下:
for(inti=0;i result[i][0]=0; } for(inti=0;i result[0][i]=0; } for(inti=1;i<=length1;i++){ for(intj=1;j<=length2;j++){ if(i-1)==(j-1)) result[i][j]=result[i-1][j-1]+1; else result[i][j]==result[i-1][j]>result[i][j-1]? result[i-1][j]: result[i][j-1]; } } 带有详细注释的代码可以在获得 Pkuacm2250Compromise动态规划题目总结(六) 这个也是求最长公共字串,只是相比CommonSubsequence需要记录最长公共字串的构成,此时箭头的标记就用上了,在程序中,用opt[][]存放标记,0表示朝向左上方,1表示指向上,-1表示指向左。 result[][]存放当前最大字串长度。 在求最优解时,顺着箭头从后向前寻找公共字串的序号,记录下来,输出即可。 该算法在算法导论中有详细的讲解。 带有详细注释的代码可以在获得。 Pkuacm1159Palindrome动态规划题目总结(七) 给一个字符串,求这个字符串最少增加几个字符能变成回文,如Ab3bd可以增加2个字符变为回文: Adb3bdA。 通过这样的结论可以和最长公共子串联系起来(未证明): S和S'(注: S'是S的反串)的最长公共子串其实一定是回文的。 这样我们就可以借助lcs来解决该题,即用s的长度减去lcs的值即可。 核心的Java代码为: total-LCS(string,newStringBuffer(string).reverse().toString()); =0或者j=0表示空字符串,所以初始化时,array[0][j]表示str1的前j个字符是否和str都匹配。 对于str=tcraete: Null c a t Null 1 0 0 0 t 1 r 0 e 0 e 0 可以证明: 当array[i-1][j](array[i][j]上面一格)和array[i][j-1](array[i][j]左面一格)都为0时,array[i][j]为0.当array[i-1][j](array[i][j]上面一格)为1且左面字母为str[i+j]时或者当array[i][j-1](array[i][j]左面一格)为1且上面字母为str[i+j]时,array[i][j]为1.这就是状态转移方程为。 核心的Java代码: if(array[i][j-1]&&(j-1)==(i+j-1)||array[i-1][j]&&(i-1)==(i+j-1)) array[i][j]=true; else array[i][j]=false; 带有详细注释的代码可以在获得 Pkuacm3356AGTC动态规划题目总结(十) 一个字符串可以插入、删除、改变到另一个字符串,求改变的最小步骤。 和最长公共子序列类似,用二维数组opt[i][j]记录字符串a中的前i个字符到字符串b中的前j个字符匹配所需要的最小步数。 假如已知AG到GT的最小步数,AGT到GT的最小步数,AG到GTT的最小步数,求AGT到GTT的最小步数,此时T==T,这个值是AG到GT的最小步数,AGT到GT的最小步数加一(AGT到GT的最小步数等于AGTT到GTT的最小步数,加一是将T删除的一步),AG到GTT的最小步数加一(AG到GTT的最小步数等于AGT到GTTT的最小步数,加一是在AGT上增加T的一步)。 假如已知AG到GT的最小步数,AGA到GT的最小步数,AG到GTT的最小步数,求AGA到GTT的最小步数,此时A! =T,这个值是AG到GT的最小步数加一(A改变为T),AGA到GT的最小步数加一(AGA到GT的最小步数等于AGAT到GTT的最小步数,加一是将T删除的一步),AG到GTT的最小步数加一(AG到GTT的最小步数等于AGA到GTTA的最小步数,加一是在GTTA上删除A的一步)。 所以状态转移方程: if(i-1)==(j-1)) array[i][j]=(array[i-1][j-1],array[i-1][j]+1),array[i][j-1]+1); else array[i][j]=(array[i-1][j-1]+1,array[i-1][j]+1),array[i][j-1]+1); 初始化的时候和最长公共子序列不同,因为第0行,第0列表示null转化到字符串情况,结果是字符串的长度: for(inti=0;i<=m;i++){ array[i][0]=i; } for(inti=0;i<=n;i++){ array[0][i]=i; } Null A G T G A T G Null 0 1 2 3 4 5 6 7 G 1 T 2 T 3 A 4 G 5 结果是array[m][n] 带有详细注释的代码可以在获得 Pkuacm1887TestingtheCATCHER动态规划题目总结(十一) 题目叙述很繁琐,其实就是求最长下降子序列,这一类题也是动态规划的典型题。 这类问题有两种算法,一种T(o)=O(n^2),另一种T(o)=O(nlogn),这里用第一种,在1631Bridgingsignals的解题报告中介绍第二种。 创建一个一维数组num_array[j],max_array[],num_array[j]表示序列的元素,max_array[i]表示以第i个元素结尾的序列中的最长下降子序列,初始化为1,对于一个max_array[i],遍历前面的每个元素j,如果num_array[j]>num_array[i]且max_array[j]>=max_array[i],那么max_array[j]就要加1,所以递推公式为: if(num_array[i]<=num_array[j]&&max_array[i]<=max_array[j]) max_array[i]++; 最后选最大的一个max_array[i]就是最长下降子序列的个数。 Java关键部分的代码: for(inti=1;i for(intj=0;j if(num_array[i]<=num_array[j]&&max_array[i]<=max_array[j]) max_array[i]++; } max_value=(max_array[i]>max_value)? max_array[i]: max_value; } max_value是最后的结果。 带有详细注释的代码可以在获得 Pkuacm2533LongestOrderedSubsequence动态规划题目总结(十二) 这个题目和1887TestingtheCATCHER一模一样,没有什么值得说的,关键的c代码如下: for(i=1;i<=n;i++) { for(j=1;j if(max[i]<=max[j]&&num[i]>num[j]) max[i]++; if(max[i]>result) result=max[i]; } printf("%d\n",result); 带有详细注释的代码可以在获得 Pkuacm1631Bridgingsignals动态规划题目总结(十三) 这个题目可以转化为最长上升子序列,这样这个题目似乎就和2533LongestOrderedSubsequence1887TestingtheCATCHER一样了,迅速写下代码,结果超时! 看来只能用O(nlogn)的算法了。 在O(n^2)的算法中: 创建一个一维数组array[j],opt[],array[j]表示序列的元素,opt[i]表示以第i个元素结尾的序列中的最长下降子序列,初始化为1,对于一个opt[i],遍历前面的每个元素j,如果array[j]>array[i]且opt[j]>=opt[i],那么opt[j]就要加1,在这里,遍历前面的每个元素j,寻找此前最大的子序列时间复杂度为O(n),如果我们在一个有序的序列中查找此前最大的序列长度,我们就可以用二分查找,时间复杂度就会降为O(logn),总的时间复杂度就会为O(nlogn)。 为此,我们增加一个一维数组B,B[i]表示当前序列为i的末尾元素的最小值。 例如对于序列: 426315: i 1 2 3 4 5 6 array 4 2 6 3 1 5 opt 1 1 2 2 1 3 B 1 3 5 构建过程如下: i=1时,opt[i]=1B[i]=4(当前为1的序列的末尾元素的最小值) opt 1 1 1 1 1 1 B 4 i=2时,2不大于4,所以opt[i]=1,将B[1]更新为2 opt 1 1 1 1 1 1 B 2 i=3时,6大于2,所以opt[i]=1+1,将B[2]更新为6 opt 1 1 2 1 1 1 B 2 6 i=4时,3在26之间,所以opt[i]=1+1,将B[2]更新为3 opt 1 1 2 2 1 1 B 2 3 i=5时,1小于2,所以opt[i]=1,将B[1]更新为1 opt 1 1 2 2 1 1 B 1 3 i=6时,5大于3,所以opt[i]=2+1,将B[3]更新为5 opt 1 1 2 2 1 3 B 1 3 5 opt[6]就是最后的结果。 从构建的过程可以容易的证明一下两点: B是递增的。 B是当前序列为i的末尾元素的最小值。 以上“2不大于4”,“3在26之间”等等的判断采用二分查找,所以总的时间复杂度为: O(nlogn),核心的c代码如下: for(i=1;i<=n;i++) { num=array[i]; left=1; right=Blen; while(left<=right) { mid=(left+right)/2; if(B[mid] left=mid+1; else right=mid-1; } opt[i]=left; B[left]=num; if(Blen Blen=left; if(max max=opt[i]; } printf("%d\n",max); 带有详细注释的代码可以在获得 Pkuacm1157LITTLESHOPOFFLOWERS动态规划题目总结(十四) 该题也是经典的动态规划,题目叙述的依然很麻烦,其实简化一下就是这样的: 例如下面这个例子就是: 3表示行,5表示列,然后在下面的3行5列每一行选一个数,使这3个数最大,要求选的数列数必须依次增大,就是从左上方向右下方选3个数使和最大。 35 723-5-2416 521-41023 -215-4-2020 我们用opt定义以当前Ij为结尾的花的排序的最大值,用r*(-50)表示负无穷,初始化时第一行为origin[i][j],后面为r*(-50) Opt[][] 1 2 3 4 5 1 7 23 -5 -24 16 2 -150 -150 -150 -150 -150 3 -150 -150 -150 -150 -150 从第二行开始,对于第i行第j列,对于i>=j,遍历i-1行前j列,求出当前最大值。 Opt[][] 1 2 3 4 5 1 7 23 -5 -24 16 2 -150 21+7 -4+max(7,23,-5) 10+max(7,23,-5,-24) 23+max(…) 3 -150 -150 -150 -150 -150 I=3: Opt[][] 1 2 3 4 5 1 7 23 -5 -24 16 2 -150 28 19 33 46 3 -150 -150 -4+max(-150,28) -20+max() 20+max(-150,28,19,33) 最后取第i行最大值即可,核心的c代码: for(i=2;i<=r;i++) for(j=1;j<=c;j++) if(j>=i) for(k=1;k if(opt[i][j] opt[i][j]=opt[i-1][k]+origin[i][j]; 带有详细注释的代码可以在获得 Pkuacm1088滑雪动态规划题目总结(十五) 2345 161718196 152425207 142322218 131211109 一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。 在上面的例子中,一条可滑行的滑坡为24-17-16-1。 当然25-24-23-...-3-2-1更长。 事实上,这是最长的一条。 输出最长区域的长度。 Opt[i][j]表示位置ij上最大的下降距离,如果其周围4个点存在高度比ij高,且opt没有ij大的点,则opt[i][j]=opt[周围]+1;另外,这个问题中存在大量重复问题,应该将计算的结果存储起来,避免重复的计算。 关键部分的c代码为: for(k=0;k<4;k++) { if(isIn(i+dx[k],j+dy[k])&&heigth[i][j] { intnum=dp(i+dx[k],j+dy[k]); if(opt[i][j]<=num) { opt[i][j]=num+1; } } } 其中constintdx[]={0,0,-1,1},dy[]={-1,1,0,0};表示一个点周围的4个点。 带有详细注释的代码可以在获得 Pkuacm1050TotheMax动态规划题目总结(十六) 题目的意思很简单,在一个矩阵里面找它的子矩阵,使得子矩阵数值之和到达最大。 其实就是最大子段和问题在二维空间上的推广。 先说一下一维的情况吧: 设有数组a0,a1…an,找出其中连续的子段,使它们的和达到最大。 假如对于子段: 92-162temp[i]表示以ai结尾的子段中的最大子段和。 在已知temp[i]的情况下,求temp[i+1]的方法是: 如果temp[i]>0temp[i+1]=temp[i]+ai(继续在前一个子段上加上ai),否则temp[i+1]=ai(不加上前面的子段),也就是说状态转移方程: temp[i]=(temp[i-1]>0? temp[i-1]: 0)+buf[i]; 对于刚才的例子temp: 911-52,然后取temp[]中最大的就是一维序列的最大子段。 求一维最大子段和的函数: intgetMax(intbuf[100],intn) { inttemp[101],max=n*(-127); memset(temp,0,4*(n+1)); for(inti=1;i<=n;i++) { temp[i]=(temp[i-1]>0? temp[i-1]: 0)+buf[i]; if(max max=temp[i]; } returnmax; } 下面扩展到二维的情况: 考察下面题目中的例子: 1-2-70 92-62 -41-47 -180-2 我们分别用ij表示起始行和终止行,遍历所有的可能: for(i=1;i<=n;i++) for(j=i;j<=n;j++){} 我们考察其中一种情况i=2j=4,这样就相当与选中了234三行,求那几列的组合能获得最大值,由于总是234行,所以我们可以将这3行”捆绑”起来,变为求4(9-4-1),11(8+2+1),-10(-6-4+0),7(7+2-2)的最大子段和,ok,问题成功转化为一维的情况! 带有详细注释的代码可以在获得 Pkuacm1014Dividing动态规划题目总结(十七) 刚AC了,趁热打铁,写下解题报告,这道题很早就在joj上做过,当时不知道dp,只会用很菜的方法,结果即使joj这道题仅要求10s还是会超时! 思想: 本题是找按价值均分大理石的方案是否存在,由于分配时不能破坏大理石,所以有个显而易见的剪枝: 当所有的大理石的总价
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- acm 动态 规划 总结