﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-无业бю游民-随笔分类-ACM/ICPC_DP(动态规划)</title><link>http://www.cnitblog.com/weiweibbs/category/7505.html</link><description>hobo</description><language>zh-cn</language><lastBuildDate>Tue, 27 Sep 2011 02:06:00 GMT</lastBuildDate><pubDate>Tue, 27 Sep 2011 02:06:00 GMT</pubDate><ttl>60</ttl><item><title>最长递增子序列问题的求解</title><link>http://www.cnitblog.com/weiweibbs/archive/2008/08/10/47866.html</link><dc:creator>hobo</dc:creator><author>hobo</author><pubDate>Sun, 10 Aug 2008 06:46:00 GMT</pubDate><guid>http://www.cnitblog.com/weiweibbs/archive/2008/08/10/47866.html</guid><wfw:comment>http://www.cnitblog.com/weiweibbs/comments/47866.html</wfw:comment><comments>http://www.cnitblog.com/weiweibbs/archive/2008/08/10/47866.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/weiweibbs/comments/commentRss/47866.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/weiweibbs/services/trackbacks/47866.html</trackback:ping><description><![CDATA[一， 最长递增子序列问题的描述<br>　　设L=&lt;a1,a2,&#8230;,an&gt;是n个不同的实数的序列，L的递增子序列是这样一个子序列Lin=&lt;aK1,ak2,&#8230;,akm&gt;，其中k1&lt;k2&lt;&#8230;&lt;km且aK1&lt;ak2&lt;&#8230;&lt;akm。求最大的m值。<br>　　二， 第一种算法：转化为LCS问题求解<br>　　设序列X=&lt;b1,b2,&#8230;,bn&gt;是对序列L=&lt;a1,a2,&#8230;,an&gt;按递增排好序的序列。那么显然X与L的最长公共子序列即为L的最长递增子序列。这样就把求最长递增子序列的问题转化为求最长公共子序列问题LCS了。<br>　　最长公共子序列问题用动态规划的算法可解。设Li=&lt; a1,a2,&#8230;,ai&gt;,Xj=&lt; b1,b2,&#8230;,bj&gt;，它们分别为L和X的子序列。令C[i,j]为Li与Xj的最长公共子序列的长度。则有如下的递推方程：<br>　　这可以用时间复杂度为O(n2)的算法求解，由于这个算法上课时讲过，所以具体代码在此略去。求最长递增子序列的算法时间复杂度由排序所用的O(nlogn)的时间加上求LCS的O(n2)的时间，算法的最坏时间复杂度为O(nlogn)＋O(n2)＝O(n2)。<br>　　三， 第二种算法：动态规划法<br>　　设f(i)表示L中以ai为末元素的最长递增子序列的长度。则有如下的递推方程：<br>　　这个递推方程的意思是，在求以ai为末元素的最长递增子序列时，找到所有序号在L前面且小于ai的元素aj，即j&lt;i且aj&lt;ai。如果这样的元素存在，那么对所有aj,都有一个以aj为末元素的最长递增子序列的长度f(j)，把其中最大的f(j)选出来，那么f(i)就等于最大的f(j)加上1，即以ai为末元素的最长递增子序列，等于以使f(j)最大的那个aj为末元素的递增子序列最末再加上ai；如果这样的元素不存在，那么ai自身构成一个长度为1的以ai为末元素的递增子序列。<br>　　这个算法由Java实现的代码如下：<br>　　public void lis(float[] L)<br>　　 {<br>　　 int n = L.length;<br>　　 int[] f = new int[n];//用于存放f(i)值；<br>　　 f[0]=1;//以第a1为末元素的最长递增子序列长度为1；<br>　　 for(int i = 1;i&lt;n;i++)//循环n-1次<br>　　 {<br>　　 f[i]=1;//f[i]的最小值为1；<br>　　 for(int j=0;j&lt;i;j++)//循环i 次<br>　　 {<br>　　 if(L[j]&lt;L[i]&amp;&amp;f[j]&gt;f[i]-1)<br>　　 f[i]=f[j]+1;//更新f[i]的值。<br>　　 }<br>　　 }<br>　　 System.out.println(f[n-1]); <br>　　 }<br>　　这个算法有两层循环，外层循环次数为n-1次，内层循环次数为i次，算法的时间复杂度<br>　　所以T(n)=O(n2)。这个算法的最坏时间复杂度与第一种算法的阶是相同的。但这个算法没有排序的时间，所以时间复杂度要优于第一种算法。<br>　　四， 对第二种算法的改进<br>　　在第二种算法中，在计算每一个f(i)时，都要找出最大的f(j)(j&lt;i)来，由于f(j)没有顺序，只能顺序查找满足aj&lt;ai最大的f(j)，如果能将让f(j)有序，就可以使用二分查找，这样算法的时间复杂度就可能降到O(nlogn)。于是想到用一个数组B来存储&#8220;子序列的&#8221;最大递增子序列的最末元素，即有<br>　　B[f(j)] = aj<br>　　在计算f(i)时，在数组B中用二分查找法找到满足j&lt;i且B[f(j)]=aj&lt;ai的最大的j,并将B[f[j]+1]置为ai。下面先写出代码，再证明算法的证明性。用Java实现的代码如下：<br>　　lis1(float[] L)<br>　　{<br>　　 int n = L.length;<br>　　 float[] B = new float[n+1];//数组B；<br>　　 B[0]=-10000;//把B[0]设为最小，假设任何输入都大于-10000；<br>　　 B[1]=L[0];//初始时，最大递增子序列长度为1的最末元素为a1<br>　　 int Len = 1;//Len为当前最大递增子序列长度，初始化为1；<br>　　 int p,r,m;//p,r,m分别为二分查找的上界，下界和中点；<br>　　 for(int i = 1;i&lt;n;i++)<br>　　 {<br>　　 p=0;r=Len;<br>　　 while(p&lt;=r)//二分查找最末元素小于ai+1的长度最大的最大递增子序列；<br>　　 {<br>　　 m = (p+r)/2;<br>　　 if(B[m]&lt;L[i]) p = m+1;<br>　　 else r = m-1;<br>　　 }<br>　　 B[p] = L[i];//将长度为p的最大递增子序列的当前最末元素置为ai+1;<br>　　 if(p&gt;Len) Len++;//更新当前最大递增子序列长度；<br>　　 <br>　　 <br>　　 }<br>　　 System.out.println(Len); <br>　　}<br>
<img src ="http://www.cnitblog.com/weiweibbs/aggbug/47866.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/weiweibbs/" target="_blank">hobo</a> 2008-08-10 14:46 <a href="http://www.cnitblog.com/weiweibbs/archive/2008/08/10/47866.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>