动态规划多种题型

Posted 见字如面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划多种题型相关的知识,希望对你有一定的参考价值。

                                                                                  数字三角形

                                           数塔

Problem Description

在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:

有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

已经告诉你了,这是个DP的题目,你能AC吗?
Input
输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。
Output
对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。
Sample Input
1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30
这是我大一写的第一道动态规划了
我们用dp[i][j]表示从起始点到i,j位置的最大数字和 ,转移方程便是dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];
其实这还可以从下往上推 那么到起点的数字之和一定是最大的 我这个就是从下往上推的
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<map>
 8 #include<set>
 9 #include<vector>
10 #include<cstdlib>
11 #include<string>
12 #define eps 0.000000001
13 typedef long long ll;
14 typedef unsigned long long LL;
15 using namespace std;
16 const int N=100+10;
17 int mp[N][N];
18 int dp[N][N];
19 int main(){
20     int t;
21     int n;
22     scanf("%d",&t);
23     while(t--){
24         scanf("%d",&n);
25         for(int i=1;i<=n;i++)
26         for(int j=1;j<=i;j++)scanf("%d",&mp[i][j]);
27         int maxx=0;
28         for(int i=n-1;i>=1;i--)
29         for(int j=1;j<=i;j++){
30             mp[i][j]+=max(mp[i+1][j],mp[i+1][j+1]);
31         }
32         cout<<mp[1][1]<<endl;
33     }
34 }

                                           矩形嵌套 (DAG)

有n个矩形  每个矩形有长和宽a b;要使x矩形可以嵌套在y矩形内  必须(x.a<y.a&&x.b<y.b 或者 x.a<y.b&&x.b<y.a);

现在问你选出最多的矩形排成一行 使除了最后以后矩形之外  每个矩形都可以嵌套在下一个矩形

                               矩形嵌套

描述
有n个矩形,每个矩形可以用a,b来描述,表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d或者b<c,a<d(相当于旋转X90度)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。
输入
第一行是一个正正数N(0<N<10),表示测试数据组数,
每组测试数据的第一行是一个正正数n,表示该组测试数据中含有矩形的个数(n<=1000)
随后的n行,每行有两个数a,b(0<a,b<100),表示矩形的长和宽
输出
每组测试数据都输出一个数,表示最多符合条件的矩形数目,每组输出占一行
样例输入
1
10
1 2
2 4
5 8
6 10
7 9
3 1
5 8
12 10
9 7
2 2
样例输出
5

题解:首先我们先让每个矩形的长的边 按照从大到小的排个序  Vis[i][j]表示第j个矩形可以嵌套在第i个矩形内

所以我们可以遍历比较一遍  最终根据vis[i][j]得到的就是一个有向无环图

dp[i]表示从节点i出发的最长路长度 ,那么转移方程就是dp[i]=max(dp[j]+1,vis[i][j]=1)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<map>
 5 #include<cstdlib>
 6 #include<vector>
 7 #include<set>
 8 #include<queue>
 9 #include<cstring>
10 #include<string.h>
11 #include<algorithm>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 typedef unsigned long long LL;
15 using namespace std;
16 const int N=1000+10;
17 int vis[1005][1006];
18 struct node{
19     int x,y;
20 }a[N];
21 int ans[N];
22 bool cmp(node xx,node yy){
23     if(xx.x==yy.x)return xx.y>yy.y;
24     else
25         return xx.x>yy.x;
26 }
27 int main(){
28     int t;
29     scanf("%d",&t);
30     while(t--){
31         memset(ans,0,sizeof(ans));
32         memset(vis,0,sizeof(vis));
33         int n;
34         scanf("%d",&n);
35         // scanf("%d",&n);
36         for(int i=0;i<n;i++){
37             scanf("%d%d",&a[i].x,&a[i].y);
38             if(a[i].x<a[i].y)swap(a[i].x,a[i].y);
39         }
40         sort(a,a+n,cmp);
41         for(int i=0;i<n;i++)
42         for(int j=i+1;j<n;j++){
43             if(a[i].x>a[j].x&&a[i].y>a[j].y)vis[j][i]=1;
44         }
45         int maxx=0;
46         for(int i=0;i<n;i++){
47             ans[i]=1;
48             for(int j=0;j<n;j++){
49                 if(vis[i][j]==1&&ans[i]<ans[j]+1)
50                 ans[i]=ans[j]+1;
51             }
52          }
53        // for(int i=0;i<n;i++)cout<<ans[i]<<" ";
54        // cout<<endl;
55         for(int i=0;i<n;i++)maxx=max(ans[i],maxx);
56         cout<<maxx<<endl;
57     }
58 }

                                         最长公共字串和最长公共子序列

拿例子来看吧  X:bacabcefabc   Y: baeabcghabc

最长公共字串:abc(字符串必须是连续的 )

最长公共子序列:abcabc(可以不连续)

<1>  最长公共字序列:dp[i][j] 表示储存Xi Yj的最长公共子序列长度

假如 i=0或者j=0 dp[i][j]=0;

假如 i>0&&j>0 Xi==Yj dp[i][j]=dp[i-1][j-1]+1;

假如i>0&&j>0  Xi!=Yj  dp[i][j]=max(dp[i-1][j],dp[i][j-1])(自己想想应该很好理解的);

来看一道题:

                             最长公共子序列

时间限制:3000 ms  |  内存限制:65535 KB
难度:3
描述
咱们就不拐弯抹角了,如题,需要你做的就是写一个程序,得出最长公共子序列。
tip:最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
输入
第一行给出一个整数N(0<N<100)表示待测数据组数
接下来每组数据两行,分别为待测的两组字符串。每个字符串长度不大于1000.
输出
每组测试数据输出一个整数,表示最长公共子序列长度。每组结果占一行。
样例输入
2
asdf
adfsd
123abc
abc123abc
样例输出
3
6
代码板子

<2>最长公共字串

当i==0或者j==0 dp[i-1][j-1]=0;

当X[i-1]==Y[j-1]  dp[i][j]=dp[i-1][j-1]+1;

否则  dp[i-1][j-1]=0;

 

                                         最大矩阵问题

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<map>
 5 #include<cstdlib>
 6 #include<vector>
 7 #include<set>
 8 #include<queue>
 9 #include<cstring>
10 #include<string.h>
11 #include<algorithm>
12 #define INF 0x3f3f3f3f
13 typedef long long ll;
14 typedef unsigned long long LL;
15 using namespace std;
16 const int N=500+50;
17 int dp[N][N];
18 int m,n;
19 int main(){
20     while(scanf("%d%d",&m,&n)!=EOF){
21         memset(dp,0,sizeof(dp));
22         int x;
23         int maxx=-555555;
24         int t=-66666;
25         for(int i=1;i<=m;i++)
26         for(int j=1;j<=n;j++){
27             scanf("%d",&x);
28             t=max(t,x);
29             dp[i][j]=dp[i-1][j]+x;
30         }
31 
32         for(int i=1;i<=m;i++)
33         for(int j=i;j<=m;j++){
34             int sum=0;
35             for(int k=1;k<=n;k++){
36                 sum=sum+dp[j][k]-dp[i-1][k];
37                 if(sum<0)sum=0;
38                 if(sum>maxx)maxx=sum;
39             }
40         }
41         if(maxx==0)cout<<t<<endl;
42         else
43 
44         cout<<maxx<<endl;
45     }
46 }

 

 

以上是关于动态规划多种题型的主要内容,如果未能解决你的问题,请参考以下文章

动态规划经典题型

动态规划·题型归纳

动态规划面试题型归类机

清华学霸的解题秘籍,拿下动态规划就4步!

各题型归纳总结

各题型归纳总结