8月3号的LCS,LIS,LICS:Longest Ordered Subsequence&&Common Subsequence&&Greatest Common Increasing Subsequence

Longest Ordered Subsequence  HDU2533

求最长递增子序列的模板:

Slyar:属于简单的经典的DP,求最长上升子序列(LIS)。先研究了O(n^2)的思路。

令A[i]表示输入第i个元素,D[i]表示从A[1]到A[i]中以A[i]结尾的最长子序列长度。对于任意的0 <  j <= i-1,如果A(j) < A(i),则A(i)可以接在A(j)后面形成一个以A(i)结尾的新的最长上升子序列。对于所有的 0 <  j <= i-1,我们需要找出其中的最大值。

DP状态转移方程:

D[i] = max{1, D[j] + 1} (j = 1, 2, 3, ..., i-1 且 A[j] < A[i])

解释一下这个方程,i, j在范围内:

如果 A[j] < A[i] ,则D[i] = D[j] + 1

如果 A[j] >= A[i] ,则D[i] = 1

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<algorithm>
 4 using namespace std;
 5 int main()
 6 {
 7     int n,i,j,MAX,dp[2005],a[2005];
 8     while(scanf("%d",&n)!=EOF)
 9     {
10         MAX=0;
11         for(i=0;i<n;i++)
12             scanf("%d",&a[i]);
13         for(i=0;i<n;i++)
14         {
15             dp[i]=1;
16             for(j=0;j<i;j++)
17                 if(a[i]>a[j]&&dp[i]<dp[j]+1)
18                     dp[i]=dp[j]+1;
19             if(dp[i]>MAX)
20                 MAX=dp[i];
21         }
22         printf("%d
",MAX);
23     }
24     return 0;
25 }

还有可以用二分法找最长子序列,但具体是多少求不出。

 1 #include<iostream>
 2 #include<string.h>
 3 #include<stdio.h>
 4 #include<algorithm>
 5 using namespace std;
 6 int b[1005];
 7 int main()
 8 {
 9     int n,a,len,left,right,mid,i;
10     while(scanf("%d",&n)!=EOF)
11     {
12         len=0;
13         memset(b,0,sizeof(b));
14         b[0]=-1;
15         for(i=1;i<=n;i++)
16         {
17             scanf("%d",&a);
18             if(a>b[len])
19             {
20                 len++;
21                 b[len]=a;
22             }
23             else if(a<b[len])
24             {
25                 left=0;
26                 right=len;
27                 while(right-left>1)
28                 {
29                     mid=(left+right)/2;
30                     if(a<=b[mid])
31                         right=mid;
32                     else
33                         left=mid;
34                 }
35                 b[right]=a;
36             }
37         }
38         printf("%d
",len);
39     }
40 }

Common Subsequence  HDU 1159

给出求最长公共子序列的模板:

设两个字符串为a[]b[],dp[i][j]为a取前i个,b取前j个字符时的最大公共子序列。
动态方程:
if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=(dp[i][j-1]>p[j][i-1]?dp[i][j-1]:p[j][i-1]);
初始化时a[i][0]=a[0][j]=0;
 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<algorithm>
 4 #include<string.h>
 5 using namespace std;
 6 char a[1005],b[1005];
 7 int dp[1005][1005];
 8 int main()
 9 {
10     int n,m,i,j;
11     while(scanf("%s%s",a+1,b+1)!=EOF)
12     {
13         n=strlen(a+1);
14         m=strlen(b+1);
15         memset(dp,0,sizeof(dp));
16         for(i=1;i<=n;i++)
17             for(j=1;j<=m;j++)
18         {
19             if(a[i]==b[j])
20                 dp[i][j]=dp[i-1][j-1]+1;
21             else if(dp[i][j-1]>dp[i-1][j])
22                 dp[i][j]=dp[i][j-1];
23             else
24                 dp[i][j]=dp[i-1][j];
25         }
26         printf("%d
",dp[n][m]);
27     }
28     return 0;
29 }

Greatest Common Increasing Subsequence HDU 1423

第三个模板:(俩者的综合)

 1     #include<iostream>
 2     #include<stdio.h>
 3     #include<algorithm>
 4     #include<string.h>
 5     using namespace std;
 6     int a[1005],b[1005],f[1005];
 7     int main()
 8     {
 9         int t,m,n,i,j,max1;
10         scanf("%d",&t);
11         while(t--)
12         {
13             scanf("%d",&m);
14             for(i=0;i<m;i++)
15                 scanf("%d",&a[i]);
16             scanf("%d",&n);
17             for(i=0;i<n;i++)
18                 scanf("%d",&b[i]);
19             memset(f,0,sizeof(f));
20             for(i=0;i<m;i++)
21             {
22                 max1=0;
23                 for(j=0;j<n;j++)
24                 {
25                     if(a[i]>b[j]&&max1<f[j])
26                         max1=f[j];
27                     if(a[i]==b[j])
28                         f[j]=max1+1;
29                 }
30 
31             }
32             max1=0;
33             for(i=0;i<n;i++)
34                 if(f[i]>max1)
35                     max1=f[i];
36             printf("%d
",max1);
37             if(t) printf("
");
38         }
39         return 0;
40     }

Palindrome HDU 1513

回文字符:就是把它调换,求最长公共序列。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<stdio.h>
 4 #include<string.h>
 5 using namespace std;
 6 char a[5005],b[5005];
 7 int c1[2][5005];//注意压缩路径!!减小内存(用到滚动数组):常用于DP中,因一个式子只由前面俩个或三个推出来的,所以可以压缩求出结果(但是此方法只能求出最后结果,中间的任何情况被来回的取模给淹没了!!!)
 8 int main()
 9 {
10     int n,i,j;
11     while(scanf("%d",&n)!=EOF)
12     {
13         scanf("%s",a+1);
14         n=strlen(a+1);
15         for(i=1;i<=n;i++)
16             b[i]=a[n+1-i];
17         memset(c1,0,sizeof(c1));
18         for(i=1;i<=n;i++)
19             for(j=1;j<=n;j++)
20         {
21             if(a[i]==b[j])
22                 c1[i%2][j]=c1[(i-1)%2][j-1]+1;
23             else if(c1[i%2][j-1]>c1[(i-1)%2][j])
24                 c1[i%2][j]=c1[i%2][j-1];
25             else
26                 c1[i%2][j]=c1[(i-1)%2][j];
27         }
28         printf("%d
",n-c1[n%2][n]);
29     }
30     return 0;
31 }

魔法串 HDU 4545

一个求最长公共子序列的变行:

题意:多了一个条件:第二个可以字符变换。

 1 #include<iostream>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<stdio.h>
 5 using namespace std;
 6 char a[1005],b[1005],a1[2],b1[2];
 7 bool s[1005][1005];//竟然可以这么用(布尔函数)
 8 int dp[1005][1005];
 9 int main()
10 {
11     int d=0,t,x,n,m,i,j;
12     scanf("%d",&t);
13     while(t--)
14     {
15         scanf("%s %s",a+1,b+1);
16         n=strlen(a+1);
17         m=strlen(b+1);
18         memset(dp,0,sizeof(dp));
19         memset(s,0,sizeof(s));
20         scanf("%d",&x);
21         while(x--)
22         {
23             scanf("%s %s",a1,b1);
24             s[a1[0]][b1[0]]=1;//这里就简化了很多步骤!!
25         }
26         for(i=1;i<=m;i++)
27             for(j=1;j<=n;j++)
28         {
29             if(b[i]==a[j]||s[b[i]][a[j]])
30                     dp[i][j]=dp[i-1][j-1]+1;
31             else if(dp[i][j-1]>dp[i-1][j])
32                 dp[i][j]=dp[i][j-1];
33             else
34                 dp[i][j]=dp[i-1][j];
35 
36         }
37         if(dp[m][n]==n)
38             printf("Case #%d: happy
",++d);
39         else
40             printf("Case #%d: unhappy
",++d);
41     }
42     return 0;
43 }

最少拦截系统 HDU 1257

这个反映了求这类问题的核心思路:(最长递减子序列和最长递增序列在这个问题可以互换)

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<algorithm>
 4 using namespace std;
 5 int main()
 6 {
 7     int n,i,j,MAX,dp[2005],a[2005];
 8     while(scanf("%d",&n)!=EOF)
 9     {
10         MAX=0;
11         for(i=0;i<n;i++)
12             scanf("%d",&a[i]);
13         for(i=0;i<n;i++)
14         {
15             dp[i]=1;
16             for(j=0;j<i;j++)
17             {
18                 if(a[i]>a[j]&&dp[i]<dp[j]+1)
19                     dp[i]=dp[j]+1;
20             }
21             if(dp[i]>MAX)
22                 MAX=dp[i];
23         }
24         printf("%d
",MAX);
25     }
26     return 0;
27 }

貌似这题目太水了!!你求出最长递增子序列的长度和最长不递减子序列的长度都可以AC掉!!!

 1 #include<cstdio>
 2 
 3 int main()
 4 {
 5     int n,i,j,num,h[1000],max[1000];
 6     while(~scanf("%d",&n))
 7     {
 8         num=0;
 9         for(i=0;i<n;++i)
10         {
11             scanf("%d",&h[i]);
12             max[i]=1;
13         }
14         for(i=1;i<n;++i)
15             for(j=0;j<i;++j)
16             {
17                 if(h[j]<=h[i]&&max[j]+1>max[i])
18                     max[i]=max[j]+1;
19                 if(num<max[i])
20                     num=max[i];
21             }
22         printf("%d
",num);
23     }
24     return 0;
25 }

Nested Dolls HDU 1677

貌似这个题目和上面的类型一样,更加深刻结题思路。

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<algorithm>
 4 #include<string.h>
 5 using namespace std;
 6 struct line
 7 {
 8     int x;
 9     int y;
10 }a[20005];
11 bool comp1(line a,line b)
12 {
13     if(a.x!=b.x) return a.x>b.x;
14     return a.y<b.y;
15 }
16 int b[20005];
17 int main()
18 {
19     int t,i,left,right,mid,len,n;
20     scanf("%d",&t);
21     while(t--)
22     {
23         scanf("%d",&n);
24         for(i=0;i<n;i++)
25             scanf("%d%d",&a[i].x,&a[i].y);
26         sort(a,a+n,comp1);
27         len=0;b[0]=-1;
28         for(i=0;i<n;i++)
29         {
30             if(a[i].y>=b[len])
31             {
32                 len++;
33                 b[len]=a[i].y;
34             }
35             else
36             {
37                 left=0;
38                 right=len;
39                 while(right-left>1)
40                 {
41                     mid=(left+right)/2;
42                     if(b[mid]>a[i].y)
43                         right=mid;
44                     else
45                         left=mid;
46                 }
47                 b[right]=a[i].y;
48             }
49         }
50         printf("%d
",len);
51     }
52     return 0;
53 }//二分法减少时间!

吉哥系列故事――完美队形I   HDU 4512

这个题目看起来好像有思路,但就无从下手!

见大牛的:http://blog.csdn.net/freezhanacmore/article/details/9746771

解决方法:每次枚举只重叠一个人。。。重叠的人的编号 i 从 1 到 n-1【这样就一定保证了经过后面的处理,不会出现过多的重叠】
                  那么每次求 LCIS 时 a[] 对应从 1 到 i
                                                  b[] 对应从 1 到 n-i+1
                  那么每次更新 ans 最多只可能重叠了一人。【也就是题目中说的最终完美队列中有奇数个人】
                  然后再根据所求的最大 dp[i][j] 的编号 i 和 j 判断是否重叠了
                  如果对应于当前最大的 dp[i][j] :i < n-j+1 说明当前最高的人没有重叠, ans = max(ans, dp[i][j]*2)
                                                                   否则最高的人重叠了, ans = max(ans, dp[i][j]*2-1)
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 const int maxn = 210;
 8 int a[maxn], b[maxn];
 9 int dp[maxn];
10 int n;
11 
12 int LCIS(int n,int m)
13 {
14     memset(dp,0,sizeof(dp));
15     for(int i = 1; i <= n; i++)
16     {
17         int tmp = 0;
18         for(int j = 1; j <= m; j++)
19         {
20             if(a[i] > b[j])
21                 tmp = max(tmp,dp[j]);
22             else if(a[i] == b[j])
23                 dp[j] = max(dp[j],tmp+1);
24         }
25     }
26     int ans = 0;
27     for(int i = 1; i <= m; i++)
28         ans = max(ans, dp[i]);
29     return ans;
30 }
31 
32 int main()
33 {
34     int T;
35     scanf("%d", &T);
36     while(T--)
37     {
38         scanf("%d", &n);
39         for(int i = 1; i <= n; i++)
40         {
41             scanf("%d", &a[i]);
42             b[n-i+1] = a[i];
43         }
44         int ans = 1;
45         for(int i = 1; i < n; i++)
46         {
47             ans = max(ans, 2*LCIS(i, n-i)); //不重叠
48             ans = max(ans, 2*LCIS(i+1, n-i)-1); //最坏恰好重叠一人
49         }
50         printf("%d
", ans);
51     }
52     return 0;
53 }
原文地址:https://www.cnblogs.com/tt123/p/3236479.html