序列型动规模板

最长上升子序列(lis)


 1.O(n^2)

 1 #include<cstdio>
 2 #include<algorithm>
 3 using std :: max;
 4 
 5 const int maxn=100005;
 6 int a[maxn],dp[maxn],pre[maxn];
 7 int n;
 8 
 9 void outit(int now)//输出lis 
10 {
11     if(pre[now]) outit(pre[now]);
12     printf("%d ",a[now]);
13 }
14 
15 void solve()
16 {
17     for(int i=1;i<=n;i++)
18     {
19         dp[i]=1;pre[i]=0;
20         for(int j=1;j<i;j++)
21         {
22             if(a[j]<a[i]&&dp[j]+1>dp[i])
23             {
24                 dp[i]=dp[j]+1;
25                 pre[i]=j;
26             }
27         }
28     }
29     int ans=0, last;
30     for(int i=1;i<=n;i++)
31     {
32         if(dp[i]>ans)
33         {
34             ans=dp[i];
35             last=i;
36         }
37     }
38     printf("%d\n",ans);
39     outit(last);
40 }
41 
42 void init()
43 {
44     scanf("%d",&n);
45     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
46 }
47 
48 int main()
49 {
50     freopen("lis.in","r",stdin);
51     freopen("lis.out","w",stdout);
52     init();
53     solve();
54     fclose(stdin);
55     fclose(stdout);
56     return 0;
57 }
View Code

2.O(nlogn)

 1 #include<cstdio>
 2 #include<algorithm>
 3 
 4 const int maxn=10005,INF=0x7fffffff;
 5 struct node
 6 {
 7     int x,num;
 8     node(){}
 9     node(int a,int b) : x(a), num(b) {}
10     bool operator < (const node &a) const { return x>a.x; }
11 }dp[maxn];
12 int a[maxn],pre[maxn];
13 int n;
14 
15 int bsearch(int l,int r,int v)
16 {
17     while(l<r)
18     {
19         int m=l+(r-l)/2;
20         if(dp[m].x>=v) r=m;
21         else l=m+1;
22     }
23     return l;
24 }
25     
26 void outit(int now)//输出lis 
27 {
28     if(pre[now]) outit(pre[now]);
29     printf("%d ",a[now]);
30 }
31 
32 void solve()
33 {
34     for(int i=1;i<=n+1;i++) dp[i].x=INF;
35     for(int i=1;i<=n;i++)
36     {
37         int idx=bsearch(1,n,a[i]);
38         dp[idx].x=a[i];
39         dp[idx].num=i;
40         pre[i]=dp[idx-1].num;
41     }
42     int ans=bsearch(1,n+1,INF)-1;
43     printf("%d\n",ans);
44     outit(dp[ans].num);
45 }
46 
47 void init()
48 {
49     scanf("%d",&n);
50     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
51 }
52 
53 int main()
54 {
55     freopen("lis.in","r",stdin);
56     freopen("lis.out","w",stdout);
57     init();
58     solve();
59     fclose(stdin);
60     fclose(stdout);
61     return 0;
62 }
View Code

 

 

最长公共子序列(lcs)


O(n^2).暂时没有输出lcs的部分.

 1 #include<cstdio>
 2 #include<algorithm>
 3 using std :: max;
 4 
 5 const int maxn=10005;
 6 int n,m;
 7 int a[maxn],b[maxn];
 8 int dp[maxn][maxn];
 9 
10 void solve()
11 {
12     for(int i=1;i<=n;i++)
13     {
14         for(int j=1;j<=m;j++)
15         {
16             if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1;
17             else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
18         }
19     }
20     printf("%d\n",dp[n][m]);
21 }
22 
23 void init()
24 {
25     scanf("%d%d",&n,&m);
26     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
27     for(int i=1;i<=m;i++) scanf("%d",&b[i]);
28 }
29 
30 int main()
31 {
32     freopen("lcs.in","r",stdin);
33     freopen("lcs.out","w",stdout);
34     init();
35     solve();
36     fclose(stdin);
37     fclose(stdout);
38     return 0;
39 }
View Code

 

最长公共上升子序列(lcis)


状态转移方程:

a[i]==b[j]时:dp[j]=max(dp[k]+1) (1<=k<j&&b[k]<b[j])

dp[j]表示以b[j]结尾的lcis,a串最为外层循环,当a串到a[i]位置,b串到b[j]位置时,dp[j]存储的是a串在a[i-1]位置,b串在b[j]位置时的最优值.此时判断a[i]是否等于b[j]:

1.a[i]!=b[j],现在的最优值等于a在a[i-1]位置时的最优值,即dp[j]不变. 

2.a[i]==b[j],dp[j]=max(dp[k]+1) (1<=k<j&&b[k]<b[j]).

对于这个k,如果到了a[i]==b[j]时再返回从b[1]~b[j-1]找就O(n^3)了,所以在到达b[j]之前就记录最优的dp[k]与k.a[i]==b[j]时,b[k]<b[j]=a[i],

所以在a[i]的循环中,对于b串当前的b[j]位置,如果b[j]<a[i]&&maxk(最优dp[k])<dp[j],那么maxk=dp[j],k=j.

ps.因为要输出lcis,所以ans不记录最优dp[j],而是最优dp[j]对应的j.

 1 #include<cstdio>
 2 
 3 const int maxn=10005;
 4 int n,m;
 5 int a[maxn],b[maxn],dp[maxn],pre[maxn];
 6 
 7 void print_lcis(int now)//输出lcis 
 8 {
 9     if(pre[now]) print_lcis(pre[now]);
10     printf("%d ",b[now]);
11 }
12 
13 void solve()
14 {
15     int ans;//lcis在b中的末位置 
16     for(int i=1;i<=n;i++)
17     {
18         int maxk=0,k=0;//到当前j为止满足1<=k<j且b[k]<b[j]的dp[k]的值以及k 
19         for(int j=1;j<=m;j++)
20         {
21             if(b[j]<a[i]&&dp[j]>maxk) { maxk=dp[j]; k=j; }
22             if(b[j]==a[i])
23             {
24                 dp[j]=maxk+1;
25                 pre[j]=k;
26                 if(dp[j]>dp[ans]) ans=j;
27             }
28         }
29     }
30     printf("%d\n",dp[ans]);
31     print_lcis(ans);
32 }
33 
34 void init()
35 {
36     scanf("%d%d",&n,&m);
37     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
38     for(int i=1;i<=m;i++) scanf("%d",&b[i]);
39 }
40 
41 int main()
42 {
43     freopen("lcis.in","r",stdin);
44     freopen("lcis.out","w",stdout);
45     init();
46     solve();
47     fclose(stdin);
48     fclose(stdout);
49     return 0;
50 }
View Code

 

转载于:https://www.cnblogs.com/Sunnie69/p/5422006.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值