单调递增最长子序列 (NYOJ 17) [动态规划]
2018-02-05 10:57
267 查看
单调递增最长子序列
时间限制:3000 ms | 内存限制:65535 KB难度:4
描述求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4
输入第一行一个整数0<n<20,表示有n个字符串要处理
随后的n行,每行有一个字符串,该字符串的长度不会超过10000输出输出字符串的最长递增子序列的长度样例输入
3 aaa ababc abklmncdefg
样例输出
1 3 7
本题有多种解法:一:最长公共子序列法此方法用时较长,占用内存也较大,且仅适用于有规律且有限长度的子序列中,代码如下:
#include<stdio.h> #include<string.h> #define max(a,b) (a>b?a:b) int dp[10010][30]; int main(void) { char a[10010]; char b[30]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}; int n,len,i,j,k; int sum,max; scanf("%d",&n); while(n--) { scanf("%s",a); len=strlen(a); for(i=1;i<=len;i++) { for(j=1;j<=26;j++) { if(a[i-1]==b[j-1]) { dp[i][j]=dp[i-1][j-1]+1; } else { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); } } } /* for(i=0;i<len;i++) { for(j=0;j<26;j++) { printf("%d ",dp[i][j]); } printf("\n"); } */ printf("%d\n",dp[len][26]); } return 0; }
二:O(n^2) 最大子段和法
此方法采用了标记数组,记录每个当前状态下的最优解。
本题中的核心代码就是那两个for循环来更新m和dp数组中的值,
其中第一个循环中,每次都须将m置零 ,
其中第二个for循环的意思就是更新m的值,
使m的值为i位置字符之前的所有比i位置字符小的dp数组中所存值得最大值,直至第二个循环
结束,使i位置的dp数组为m的值加上1,即可。
代码如下:
#include<stdio.h> #include<string.h> char s[10010]; int dp[10010]={1}; int main(void) { int n,len; int i,j,m; scanf("%d",&n); while(n--) { scanf("%s",s); len=strlen(s); for(i=1;i<len;i++) { m=0; for(j=i-1;j>=0;j--) { if(s[i]>s[j]&&m<dp[j]) { m=dp[j]; } } dp[i]=m+1; } m=dp[0]; for(i=0;i<len;i++) { if(m<dp[i]) { m=dp[i]; } } printf("%d\n",m); } return 0; }
三:O(n^2) 此方法是方法二的优化,方法二中的标记数组过长,此方法采用标记字符串,所以标记数组的长度缩减到26即可,本代码中用的长度为30
本题中使用了标记字符串,先将待求字符串中的第一个字符赋到标记字符串中,
并使用k对标记字符串进行计数,
然后依次读入待求字符串的字符,
并与标记字符串中的字符从后往前依次比较,
若标记字符串的字符比待求字符串的字符大,且此时处在标记字符串的起始位置
,则将此位置的待求字符赋到标记字符串中,
若标记字符串的字符比待求字符串的字符小,则将此处待求字符串的字符赋到标记字符串的下一个字符,如果此时已达到标记字符串的长度,则令k加1,并结束当前循环,重新开始比较,重新开始时,只有k增加了一,其余各数据均不变。
代码如下:
#include<stdio.h> #include<string.h> int main(void) { char s[10010]; char dp[35]; int n,len,i,j,k; scanf("%d",&n); while(n--) { scanf("%s",s); len=strlen(s); dp[0]=s[0]; k=1; for(i=0;i<len;i++) { for(j=k-1;j>=0;j--) { if(dp[j]>s[i]&&j==0) { dp[0]=s[i]; } if(dp[j]<s[i]) { dp[j+1]=s[i]; if(j==k-1) { k++; } break; } } } dp[k]='\0'; printf("%d\n",k); } return 0; }
四:O(n*lg n)
此方法是对方法三的优化,
最长递增子序列可以优化到 O(n*lg n)
通过优化方法二中的s[i]在dp[]什么位置,使用二分查找。
if(a[i]>dp[k]) {k=k+1;dp[k]=s[i];}
if(a[i]<dp[1]) dp[1]=s[i];
其他情况找到 dp[j]<s[i]<dp[j+1] dp[j+1]=s[i];
注:虽然说此方法是最优的解法,但是在oj上面提交的时候,并没有方法三省时间,省内存。
#include<stdio.h> #include<string.h> #define N 10010 char s ,dp[30]; int work(int n) { int i,j,k,x,y,m; dp[0]=s[0]; k=0; for(i=1;i<n;i++) { if(dp[k]<s[i]) { k++; dp[k]=s[i]; continue; } x=0; y=k; while(x<=y) { if(s[i]<dp[x]) { j=x; break; } if(dp[y]<s[i]) { j=y+1; break; } m=(x+y)/2; if(dp[m]<s[i]) { x=m+1; } else if(s[i]<dp[m]) { y=m-1; } else { j=m; break; } } dp[j]=s[i]; } return k+1; } int main(void) { int n,i; scanf("%d",&n); while(n--) { scanf("%s",s); printf("%d\n",work(strlen(s))); } return 0; }
相关文章推荐
- NYOJ17 单调递增最长子序列(动态规划)
- NYOJ 17 单调递增最长子序列(动态规划)
- NYOJ17 单调递增最长子序列 【二分法】+【动态规划】
- nyoj17单调递增最长子序列--动态规划-初步
- NYOJ 题目17 单调递增最长子序列(动态规划)
- NYOJ-17-单调递增最长子序列【动态规划】
- 动态规划之-----单调递增最长子序列(nyoj17)
- NYOJ - 17 - 单调递增最长子序列(动态规划--LIS--单调递增最长子序列)
- NYOJ 17 单调递增最长子序列
- nyoj 17 单调递增最长子序列
- NYOJ 题目17 单调递增最长子序列
- DP入门50题(5)——NYoj 17 单调递增最长子序列
- NYOJ 17 单调递增最长子序列
- 单调递增最长子序列nyoj17
- nyoj_17_单调递增最长子序列_201403121516
- NYOJ-17 单调递增最长子序列
- nyoj 17 单调递增最长子序列
- nyoj44 nyoj17 HDU1087 DP动规 连续字符串的和最大值 单调递增最长子序列 单调递增子序列最小个数 非连续最大递增子序列
- nyoj-17 单调递增最长子序列
- nyoj17 单调递增最长子序列