【C++】动态规划:最长递增子序列和建桥问题

来源:互联网 发布:php 执行cmd命令 编辑:程序博客网 时间:2024/06/06 20:17

问题描述:
求一个一维数组的最长递增子序列,时间复杂度尽可能小。
例如:数组 1, -1,2,-3,4,-5,6,-7它的最长递增子序列是 1,2,4,6,最后返回4.
很经典的问题了,昨天面试的时候还被问道了,可是没答上来,囧的要死啊。
这是一个典型的动态规划问题,思路如下:定义一个数组Psub[i]用于存储从元素0到元素i的最长递增的长度,然后考虑第i+1个元素,从0到i进行比较若有i+1>i且Psub[i]+1>Psub[i+1],那么Psub[i+1]=Psub[i]+1;
代码如下:

int maxLengthSub(vector<int> nums){    int ret = 0;    vector<int> temp;  //temp[i]用于记录从0到i的最长子序列的长度    for(int i=0;i<nums.size();i++){        temp.push_back(1);        for(int j=0;j<i;j++){            if(nums[i]>nums[j] && temp[i]<temp[j]+1){                temp[i]=temp[j]+1;            }        }        if(temp[i]>ret)            ret=temp[i];    }    return ret;}

或是这样;

int maxLengthSub2(vector<int> nums){    int ret = 0;    int temp[nums.size()];  //temp[i]用于记录从0到i的最长子序列的长度    for(int i=0;i<nums.size();i++){        temp[i]=1;        for(int j=0;j<i;j++){            if(nums[i]>nums[j] && temp[i]<temp[j]+1){                temp[i]=temp[j]+1;            }        }        if(temp[i]>ret)            ret=temp[i];    }    cout<<endl;    for(auto &v:temp){        cout<<v<<"  ";    }    return ret;}

上面两个代码思路都是相同的。不同的是第一个用到了vector容器,逼格高一点点。不过用vector的时候要记得,对于一个新元素要进来的时候用:
temp.push_back(1);
而不可以直接:temp[i]=1;

一个拓展题目:
造桥问题. 原题是这样:Building Bridges. (英文题意略,写了也不会看的)大致就是要在一条河的南北两边的各个城市之间造若干座桥.桥两边的城市分别是a(1)…a(n)和b(1)…b(n).这里的要求a(i)只可以和b(i)之间造桥,同时两座桥之间不能交叉.希望可以得到一个尽量多座桥的方案.如下图所示:
这里写图片描述
感觉拿到手一脸的蒙蔽状,不知道这个这东西到底跟动态规划有毛的关系?不要急,有个不要脸的曾说是过:急事缓做。咱们一步步的分析:
首先上北下南;对于南边的编号:s1={2,1,3,5,4};北边的编号:s2={1,2,5,4,3};然后从南边的第一座桥开始计算在北边序列中的index.也就是S1中的每个值相对于S2中的位置.比如说A2在南边是第一个在北边是第二个,所以第一个元素是2.A1在北边的对应位置是1.A3在北边的对应位置是5,A5在北边的对应位置是3,最后一个A4在北边的对应位置是3.这样我们就得到一个新的序列s3= {2,1,5,3,4}.这个序列的实际意义就是南边的第几座桥需要连接到北边的第几座桥。
下面开始分析s3,s3是根据北面的桥所对应的位置(从左到右,从1到5排序)而得到的,也就是说s3中的数字所有一定的位置关系的,s3中的数对应着这s1的位置,比如s3中的第三个数{5}表示s1中第3个位置的数;那么是不是有点像一个以1为起始数组呢?是不是有点靠近动态规划的意思了呢?咱们再进一步的分析一下,取s3中的两个数{5,3}并假设这两个数对应的下标所{1,2},那么这{5,3}在上例建桥中就表示1与5相连,2与3相连;这样的话就会出现交叉连接的情况了,因此就不符合题意了。
呼呼,讲了半天,其实所说只有当s3中前面的数子不大于后面的数字的时候这样的桥才所可以建的,那么也就是说我们的造桥问题就转化成了寻找这个序列的最长递增子序列的问题!当当当,惊不惊喜,奇不奇妙~
代码如下:

vector<int> maxLengthSub(vector<int> nums){    int ret = 0;    vector<int> temp;  //temp[i]用于记录从0到i的最长子序列的长度    for(int i=0;i<nums.size();i++){        temp.push_back(1);        for(int j=0;j<i;j++){            if(nums[i]>nums[j] && temp[i]<temp[j]+1){                temp[i]=temp[j]+1;            }        }        if(temp[i]>ret)            ret=temp[i];    }    //上面就得到了temp存储着最长子序列的长度    //下面所获取最长子序列所对应的数    vector<int> result;    for(int i=nums.size()-1;i>0 && ret>0;i--){        if(temp[i]==ret){            result.push_back(nums[i]);            ret--;        }    }    return result;}//对数据的预处理,以及得到数据之后的后处理vector<int> GetMaxNum(vector<int> s1,vector<int> s2){    vector<int> s1Cont;    //从s1,s2中获得s1对应的下标s3    for(int i=0;i<s1.size();++i){        for(int j=0;j<s2.size();++j){            if(s1[i]==s2[j])                s1Cont.push_back(j+1);        }    }    //得到了s1所对应的最优下标。需要转换成s1的数据    vector<int> s1Index =  maxLengthSub(s1Cont);    vector<int> result;    for(int i =s1Index.size()-1;i>=0;i--){        for(int j=0;j<s1Cont.size();j++){            if(s1Index[i]==s1Cont[j]){                result.push_back(s1[j]);            }        }    }    return result;}

完整代码下载连接:http://download.csdn.net/download/w417950004/10111911

原创粉丝点击