序列动规

序列动态规划

一. 关于最长上升/下降/不上升/不下降子序列

题目大意 :给定一个序列,求当前序列中最长上升/下降/不上升/不下降子序列(序列可以不连续)

注 :以下算法皆由最长上升子序列展示      

  1. Ο(n2)

     思路 :f[i]表示以q[i]结尾的最长上升子序列的长度,初始值为1。每次循环1 ~ n,再循环1 ~ i - 1,如果q[i] > q[j](要上升啊),就f[i] = max(f[i], f[j] + 1)(这个 + 1是因为还要加上q[i]啊)

     code :

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n, q[100001], f[100001], maxl;
 4 inline void memset(int a[], int b, int l)//手写memset 
 5 {
 6     for(register int i = 1; i <= l; ++i)
 7     {
 8         a[i] = b;
 9     }
10     return;
11 }
12 signed main()
13 {
14     scanf("%d", &n);
15     for(register int i = 1; i <= n; ++i)
16     {
17         scanf("%d", &q[i]);
18     }
19     memset(f, 1, n);//初始化 
20     for(register int i = 1; i <= n; ++i)
21     {
22         for(register int j = 1; j < i; ++j)
23         {
24             if(q[i] > q[j])//要大于 
25             {
26                 f[i] = max(f[i], f[j] + 1);//取max 
27             }
28         }
29         maxl = max(maxl, f[i]);//取答案最大值 
30     }
31     printf("%d", maxl);//最后输出的是maxl,不是f[n] 
32     return 0;
33 }

  2. O(n log n)

   思路 :现在不需要整f[i]了,但整一个x数组和z,z表示最长上升子序列的长度,x里存的是最长上升子序列。注意 :此时要先把x[++z] = q[1],然后循环2 ~ n,如果当前数 > x[z]那么直接插进去就好了,否则二分查找第一个大于等于它的数,然后把那个数给替换掉,其他的不变,最后输出z。

   证明 :查找出来的数是第一个大于等于它的数,那么当前数肯定比它右边的数要小(因为u(当前数) <= x(第一个比u大的数)&& x < y(x左边的数)所以 u < y)

            查找出来的数是第一个大于等于它的数,那么既然是第一个,那一定左面的数就 <= 当前数了呗

      code(直接用STL了) :

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n, q[100001], x[100001], z;
 4 int main()
 5 {
 6     scanf("%d", &n);
 7     for(register int i = 1; i <= n; ++i)
 8     {
 9         scanf("%d", &q[i]);
10     }
11     x[++z] = q[1];
12     for(register int i = 2; i <= n; ++i)
13     {
14         if(q[i] > x[z])
15         {
16             x[++z] = q[i];
17         }
18         else
19         {
20             int p = lower_bound(x + 1, x + z + 1, q[i]) - x;
21             x[p] = q[i];
22         }
23     }
24     printf("%d", z);
25     return 0;
26 }

二.  关于最长公共子序列

题目大意:给定两个字符串x,y,求两个字符串中最长公共(及两个字符相等)子序列

        思路:设f[i][j]表示x序列到第i个字符,y序列到第j个字符的最长公共子序列。这时分两种情况,一是x[i]和y[j]这两个字符相等(及可以同时选),那么答案就 等于f[i - 1][j - 1] + 1了(及从1 ~ i - 1和1 ~ j - 1中选最长公共子序列再加上1,因为还要加上x[i],y[j]这两个相等个字符啊);二是x[i] != y[j](及不能同时选,想同时选也不能同时选啊,都不同怎么同时选),那么这是会比较麻烦,因为我们只能选其一,所以我们就在两种不同的方案里取最大值——f[i][j] = max(f[i][j - 1], f[i - 1][j])。然后……就没有然后了,就完了,直接枚举x序列和y序列的长度,进行判断,看执行哪个,最后输出f[x序列的长度][y序列的长度]就OK了

        code:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n, f[1001][1001];
 4 char x[1001], y[1001];
 5 signed main()
 6 {
 7     scanf("%s %s", x, y);
 8     int lx = strlen(x);
 9     int ly = strlen(y);
10     for(register int i = 1; i <= lx; ++i)
11     {
12         for(register int j = 1; j <= ly; ++j)
13         {
14             if(x[i - 1] == y[j - 1])//从一开始枚举的,要减一才是正确的字符啊 
15             {
16                 f[i][j] = f[i - 1][j - 1] + 1;//别忘了加一 
17             }
18             else
19             {
20                 f[i][j] = max(f[i - 1][j], f[i][j - 1]);//两种情况取max,从而得到最优值 
21             }
22         }
23     }
24     printf("%d", f[lx][ly]);//最后答案在f[lx][ly]里 
25     return 0;
26 }
posted @ 2019-07-03 22:07  louis_11  阅读(210)  评论(0编辑  收藏  举报