最长递减子序列
来源:互联网 发布:php curl跨域 编辑:程序博客网 时间:2024/06/04 18:43
最长递减子序列
1、面试题目
求一个数组的最长递减子序列比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5, 4,3,2}
2、解题方法
采用动态规划解决题目
Ø 最长公共子序列方法解决
Ø 直接根据动态转移方程解决
2.1最长公共子序列方法
2.1.1原理
设原来数组为X[1..n],而数组Y[1…n]为数组X[]的非递增序列,现在我们求X[1…n],Y[1…n]的最长公共子序列,现在简要说下最长公共子序列的原理(详解见算法导论第2版Page208):
假设序列X={x1, x2, …, xn}和Y ={y1, y2, …,yn}的最长公共子序列为Z = {z1, z2,.., zk}
1) 如果xm = yn, 则zk = xm =yn,且zk-1是xm-1与yn-1的一个最长公共子序列
2) 如果xm!= yn,且zk != xm,那么Z是xm-1与yn的一个最长公共子序列
3) 若果xm != yn,且zk != yn, 那么Z是xm与yn-1的一个最长公共子序列
所以状态转移方程如下(本来画了图表示的,但是图没有显示,暂且这样吧):
dp[i,j] = <1> 0 if i=0或者j =0
<2> dp[i-1, j-1] + 1 if i , j >0 and xi = yj
<3>max(dp[i-1][j], dp[i][j-1]) if i, j > 0 and xi != yj
根据上述状态方程,以序列X[1…n]和Y[1…n]为输入参数,输出参数有dp[i][j]和path[i][j]
其中dp[i][j]表示X[0…i]和Y[0…j]的最长公共子序列的长度,path[i][j]标志着dp[i][j]值是如何取得的,也就是最长公共子序列的路径,即
path[i, j] = <1> 1 if i , j >0 and xi = yj
<2> 2 if i, j >0 and xi != yj and dp[i-1, j] > dp[i, j-1]
<3> 3 if i, j >0 and xi != yj and dp[i-1, j] < dp[i, j-1]
最长公共子序列的代码如下:
int LCS(int X[], int xlen, int Y[], int ylen)
{
int i, j;
int maxlen;
memset(dp, 0,sizeof(dp));
memset(path, 0,sizeof(path));
maxlen = 0;
for(i = 1;i <= xlen;++i)
{
for(j = 1; j<= ylen; ++j)
{
if(X[i-1]== Y[j-1])
{
dp[i][j]= dp[i-1][j-1] +1;
path[i][j]= 1;
}
else if(dp[i-1][j] > dp[i][j-1])
{
dp[i][j]= dp[i-1][j];
path[i][j]= 2;
}
else
{
dp[i][j]= dp[i][j-1];
path[i][j]= 3;
}
if(dp[i][j]> maxlen)
{
maxlen= dp[i][j];
}
}
}
return maxlen;
}
LCS可以在O(n^2)的时间内序列X={x1,x2,..xm}和Y={y1,y2,…,yn}最长递减序列的长度,同时搜索path[][]数组可以找到最长非递增子序列, 方法如下:
1)当path[i][j]= 1,表示x[i]==y[j],那么Xi和Yj的最长公共子序列就是在Xi-1和Yj-1的基础上加上Xi或者Yj得到的
2)当path[i][j]= 2,表示x[i]==y[j],那么Xi和Yj的最长公共子序列就是Xi-1和Yj的最长公共子序列
3)当path[i][j]= 3,表示x[i]==y[j],那么Xi和Yj的最长公共子序列就是Xi和Yj-1的最长公共子序列
代码如下:
void LCS_path(int path[][NSIZ], int X[], int i, int j)
{
if(i <= 0 || j<= 0)
{
return;
}
if(path[i][j] == 1)
{
LCS_path(path,X, i-1, j-1);
printf("%d", X[i-1]);
}
else
{
if(path[i][j]== 2)
{
LCS_path(path,X, i-1, j);
}
else
{
LCS_path(path,X, i, j-1);
}
}
}
2.1.2测试
测试代码如下:
#include <iostream>#include <algorithm>using namespace std;#define NSIZ 120int dp[NSIZ][NSIZ];int path[NSIZ][NSIZ];int cmp(int a, int b){return a > b;}int LCS(int X[], int xlen, int Y[], int ylen){int i, j;int maxlen;memset(dp, 0, sizeof(dp));memset(path, 0, sizeof(path));maxlen = 0;for(i = 1;i <=xlen; ++i){for(j = 1; j<= ylen; ++j){if(X[i-1]== Y[j-1]){dp[i][j]= dp[i-1][j-1] +1;path[i][j]= 1;}else if(dp[i-1][j] > dp[i][j-1]){dp[i][j]= dp[i-1][j];path[i][j]= 2;}else{dp[i][j]= dp[i][j-1];path[i][j]= 3;}if(dp[i][j]> maxlen){maxlen= dp[i][j];}}}return maxlen;}void LCS_path(int path[][NSIZ], int X[], int i, int j){if(i <= 0 || j<= 0){return;}if(path[i][j] == 1){LCS_path(path,X, i-1, j-1);printf("%d", X[i-1]);}else{if(path[i][j]== 2){LCS_path(path,X, i-1, j);}else{LCS_path(path,X, i, j-1);}}}int main(){int X[] = {9, 4, 3,2, 5, 4, 3, 2};int Y[NSIZ];int maxlen;int n = sizeof(X)/sizeof(X[0]);memcpy(Y, X, n * sizeof(int));sort(Y, Y + n,cmp);maxlen = LCS(X, n, Y, n);LCS_path(path, X, n, n);return 0;}
2.2直接根据动态转移方程解决
2.2.1 原理
典型的DP问题,设原数组X[1…n],另设辅助数组dp[i]表示从i到n这一段中以n结束的最长递减子序列的长度,设定初始的dp[i]=1(0 <= i <n),则状态转移方程为:dp[i] = max(dp[i], dp[j]+1),0 <= i < n , i<j < n, 并且X[i] > X[j].同样时间复杂度为O(n^2);
函数代码:
int longest_decrease_seq(int X[], int n)
{
int i, j;
int maxlen;
for(i = 0;i <=n; ++i)
{
dp[i] = 1;
}
maxlen = dp[0];
for(i = n-1;i >=0; --i)
{
for(j = i + 1;j < n; ++j)
{
if(X[i]> X[j])
{
dp[i] =max(dp[i], dp[j] + 1);
if(maxlen< dp[i]) //更新最长的序列的长度。
{
maxlen= dp[i];
}
}
}
}
//输出最大递减子序列
for(j = 0; j <n; ++j)
{
if(dp[j] ==maxlen)
{
printf("%d", X[j]);
maxlen--;
}
}
return maxlen;
}
2.2.2测试
#include <iostream>using namespace std;#define NSIZ 1200int dp[NSIZ]; //d[i]表示从num[i]到num[n]之间的最长子序列的长度。int longest_decrease_seq(int X[], int n){int i, j;int maxlen;for(i = 0;i <=n; ++i){dp[i] = 1;}maxlen = dp[0];for(i = n-1;i >=0; --i){for(j = i + 1;j < n; ++j){if(X[i]> X[j]){dp[i] =max(dp[i], dp[j] + 1);if(maxlen< dp[i]) //更新最长的序列的长度。{maxlen= dp[i];}}}}//输出最大递减子序列for(j = 0; j <n; ++j){if(dp[j] ==maxlen){printf("%d", X[j]);maxlen--;}}return maxlen;}int main(){int X[] = {9, 4, 3,2, 5, 4, 3, 2};int n =sizeof(X)/sizeof(X[0]);longest_decrease_seq(X,n);return 0;}
2.3 对方法2的改进算法
2.3.1 原理
这里参考了http://qiemengdao.iteye.com/blog/1660229的博文;
在方法2中,在X[i+1…n]中寻找满足条件X[i]> X[j]时最大的dp[j]时,采用的顺序查找算法时间复杂度为O(n), 现在我们用一个数组C[1…k]保存这从第1个元素到第i个元素的最长递减序列,这样数组C[]的长度就是最后的最长递减子序列的长度。
举个例子说明:
X[] = {9,4,3,2,5,4,3,2},
1) 对于X[1]放到C[1]中,C[1]是有序的, 即C[1] = 9,只有一个元素时最长递减序列为9,clen=1;
2)对于X[2],由于X[2] < C[1]那么直接把X[2]放到C[clen++]处,此时clen=2;
同理对X[3], x[4],此时C[] ={9,4, 3, 2},那么clen=4;
3)对于X[5] =5, X[5]>C[clen-1]=2, 那么在已经排序的C中查找,则X[5]应该在C[2]位置,那么C[2]直接被替换为X[5],现在C[]={9, 5, 3, 2}, clen=4;
4)对于X[6] =4, X[5]>C[clen-1]=2, 那么在已经排序的C中查找,则X[5]应该在C[3]位置,那么C[3]直接被替换为X[6],现在C[]={9, 5, 4, 2}, clen=4;
5)对于X[7] =3, X[5]>C[clen-1]=2, 那么在已经排序的C中查找,则X[5]应该在C[4]位置,那么C[4]直接被替换为X[7],现在C[]={9, 5, 4, 3}, clen=4;
6)对于X[8] =2, X[5]<C[clen-1]=3, 那么在已经排序的C中查找,则X[5]应该直接插入C[]中,则C[clen++] =C[5]= X[8]=2,,现在C[]={9, 5, 4,3, 2}, clen=5;
至此,整个过程明白了吧。
在C[]中查找的过程,是替换原来的值而不用移动,所以利用二分查找过程,使得整个算法的时间复杂度为O(nlgn);
关键代买如下:
//注意C[]为递减序列,左边大,右边小
int binary_search(int c[], int n, int key)
{
int left,right, mid;
left = 0,right = n-1;
while(left<= right)
{
mid =left + (right -left) /2 ;
if(key== c[mid])
{
returnmid;
}
elseif (key < c[mid]) //当c[mid]大于key时,应该往右边找
{
left= mid + 1;
}
else////当c[mid]小于key时,应该往左边找
{
right= mid - 1;
}
}
returnleft;
}
int longest_decrease_seq(int X[], int n)
{
int i, j;
int clen;
intC[NSIZ];
C[0] =X[0];
clen = 1;
for(i =1;i < n; ++i)
{
if(X[i]< C[clen-1])
{
C[clen++]= X[i];
}
else
{
j= binary_search(C, clen, X[i]);
C[j]= X[i];
}
}
//输出最大递减子序列
for(i =0; i < clen; ++i)
{
printf("%d", C[i]);
}
returnclen;
}
2.3.2测试
#include <iostream>#include <vector>#include <stack>using namespace std; #define NSIZ 1200 int C[NSIZ]; //保存着从X[1]到X[i]最长公共序列 //注意C[]为递减序列,左边大,右边小int binary_search(int c[], int n, int key){ int left, right, mid; left= 0, right = n-1; while(left<= right) { mid= left + (right -left) /2 ; if(key== c[mid]) { return mid; } else if (key < c[mid]) //当c[mid]大于key时,应该往右边找 { left = mid + 1; } else////当c[mid]小于key时,应该往左边找 { right = mid - 1; } } return left;} int longest_decrease_seq(int X[], int n){ int i, j; int clen; int C[NSIZ]; C[0]= X[0]; clen= 1; for(i= 1;i < n; ++i) { if(X[i]< C[clen-1]) { C[clen++]= X[i]; } else { j= binary_search(C, clen, X[i]); C[j]= X[i]; } } //输出最大递减子序列 for(i= 0; i < clen; ++i) { printf("%d", C[i]); } return clen;} int main(){ int X[] = {9, 4, 3, 2, 5, 4, 3, 2}; int n = sizeof(X)/sizeof(X[0]); longest_decrease_seq(X,n); return 0;}
- 最长递减子序列
- 最长递减子序列
- 最长递减子序列
- 最长递减子序列
- 最长递减子序列
- pku1887 最长递减子序列
- 最长单调递减子序列
- poj1887(最长递减子序列)
- 最长递减子序列[转载]
- 最长递增/递减子序列
- 求最长递减子序列
- 最长单调递减子序列
- 最长非递减子序列
- 最长递减子序列问题
- 求最长单调递减子序列
- 求最长单调递减子序列
- hunnu 11182#最长非递减子序列
- 求最长递减子序列LCS
- 栈增长和大端/小端问题
- 安装的字体在photoshop,coreldraw,word等软件中个别字不能用的解决办法
- android 屏幕触摸事件机制
- UVa 507 Jill Rides Again
- 几个博客的地址
- 最长递减子序列
- ActiveMQ - tcp 协议发送消息
- Android AsyncQueryHandler(异步查询机制)
- 猫の国企成长记
- UVa 674 - Coin Change
- 学习PowerShell 3.0中新增的$PSDefaultParameterValues变量
- UVa 10003 Cutting Sticks
- jquery easyui datagrid 分页 详解
- ActiveMQ - tcp 协议接收消息