数字三角形问题
题意
给定一个由n行数字组成的数字三角形\。试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
对于给定的由n行数字组成的数字三角形,计算从三角形的顶至底的路径经过的数字和的最大值。
Input
输入数据的第1行是数字三角形的行数n,1≤n≤100。接下来n行是数字三角形各行中的数字。所有数字在0…99之间。
Output
输出数据只有一个整数,表示计算出的最大值.
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
思路实现:
刚看到这个题,第一反应是递归…,要不是dp的例题我就用递归来做了,不过很明显递归肯定是会超时的。就往dp的思路上靠,感觉这个题是入门级题目,思路比较简单,从最下面一行开始逐一往上进行计算
代码:
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101;
int dp[101][101];
int n;
int Sum[101][101];
int main()
{
int i, j;
cin >> n;
for (i = 1; i <= n; i++)
for (j = 1; j <= i; j++)
cin >> dp[i][j];
for (int i = 1; i <= n; i++)
Sum[n][i] = dp[n][i];
for (int i = n - 1; i >= 1; i--)
for (int j = 1; j <= i; j++)
Sum[i][j] =
max(Sum[i + 1][j], Sum[i + 1][j + 1]) + dp[i][j];
cout << Sum[1][1] << endl;
}
最长公共子序列
题意
给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到,而且每个字符的先后顺序和原串中的先后顺序一致。
Sample Input
abcfbc abfcab
programming contest
abcd mnp
Sample Output
4
2
0
思路实现:
这题老师上课也讲了,但我感觉理解的不到位,自己再总结一下
①确定状态:
设F[x][y]表示S[1…x]与T[1…y]的最长公共子序列的长度
②确定状态转移方程和边界条件:
D分三种情况来考虑:
S[x]不在公共子序列中:该情况下FxLy]=F[x-1][y]
T[y]不在公共子序列中:该情况下F[x][y]=F[x-1][y-1]
S[x]=Ty],S[x]与T[y]在公共子序列中:该情况下,F[x][y]=F[x-1][y-1]+1
F[x][y]取上述三种情况的最大值。
综上:
状态转移方程:Fx[y]=max{F[x-1ILy], FLXILY-1],FLx-11[y-11+1)其中第三种情况要满足SLx]=T[y];
边界条件:F[0][y]=0,F[x][0]=0
③程序实现:
计算F[x][y]时用到F[x-1][y-1],F[x-1][y],F[x][y-1]这些状态,它们要么在F[x][y]的上一行,要么在F[x][y]的左边。因此预处理出第0行,然后按照行从小到大、同一行按照列从小到大的顺序来计算就可以用选代法计算出来。
代码:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int MAXN = 5005;
string S, T;
int F[MAXN][MAXN];
int main()
{
cin >> S;
cin >> T;
int ls = S.length(), lt = T.length();
for (int i = 1; i <= ls; i++)
for (int j = 1; j <= lt; j++)
{
F[i][j] = max(F[i - 1][j], F[i][j - 1]);
if (S[i - 1] == T[j - 1])
F[i][j] = max(F[i][j], F[i - 1][j - 1] + 1);
}
cout << F[ls][lt] << endl;
return 0;
}
最大上升子序列
题意
一个数的序列ai,当a1 < a2 < … < aS的时候,我们称这个序
列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK<= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给
出序列中的N个整数,这些整数的取值范围都在0到10000。
输出
最长上升子序列的长度。
输入样例
7
1 7 3 5 9 4 8
输出样例
4
思路实现:
这也是典型的例题,自己总结一下子,加深下理解。
1.找子问题
“求序列的前n个元素的最长上升子序列的长度”是个子问题,但这样分解子问题,不具有“无后效性”假设F(n) = x,但可能有多个序列满足F(n) = x。有的序列的最后一个元素比 an+1小,则加上an+1就能形成更长上升子序列;有的序列最后一个元素不比an+1小……以后的事情受如何达到状态n的影响,不符合“无后效性”“求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度,一个上升子序列中最右边的那个数,称为该子序列的“终点”。
虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。
-
确定状态:
子问题只和一个变量-- 数字的位置相关。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak做为“终点”的最长上升子序列的长度。状态一共有N个。 -
找出状态转移方程:
maxLen (k)表示以ak做为“终点”的
最长上升子序列的长度那么:
初始状态:maxLen (1) = 1
maxLen (k) = max { maxLen (i):1<=i < k 且 ai < ak且 k≠1 } + 1
若找不到这样的i,则maxLen(k) = 1
maxLen(k)的值,就是在ak左边,“终点”数值小于ak ,且长度最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN =1010;
int a[MAXN]; int maxLen[MAXN];
int main()
{
int N; cin >> N;
for( int i = 1;i <= N;++i)
{
cin >> a[i];
maxLen[i] = 1;
}
for( int i = 2; i <= N; ++i)
{
//每次求以第i个数为终点的最长上升子序列的长度
for( int j = 1; j < i; ++j)
//察看以第j个数为终点的最长上升子序列
if( a[i] > a[j] )
maxLen[i] = max(maxLen[i],maxLen[j]+1);
}
cout << * max_element(maxLen+1,maxLen + N + 1 );
return 0;
}
最大子矩阵和
题意
一个MN的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值。
例如:33的矩阵:
-1 3 -1
2 -1 3
-3 1 2
和最大的子矩阵是:
3 -1
-1 3
1 2
Input
第1行:M和N,中间用空格隔开(2 <= M,N <= 500)。
第2 - N + 1行:矩阵中的元素,每行M个数,中间用空格隔开。(-10^9 <= M[i] <= 10^9)
Output
输出和的最大值。如果所有数都是负数,就输出0。
思路
也是经典例子,解法与动态规划经典题目——最大连续子序列之和题目思想一样,只不过是二维空间上的拓展。N*N矩阵用二维数组a[N][N]表示。通过思考可以发现,这道题目与动态规划经典题目——最大连续子序列之和非常相似,只不过动态规划经典题目——最大连续子序列之和它是一维的问题,我们可以把每列的元素进行合并为一个元素(可以定义二维数组sum[][],其中sum[i]表示前i行每列数字相加的和,所以sum[j][] - sum[i][]为一维向量),这样运用动态规划经典题目——最大连续子序列之和的思想了。
代码:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1000;
ll arr[N][N];
ll dp[N][N];
int m, n;
int main()
{
while (cin>>m>>n)
{
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> arr[i][j];
dp[i][j] = dp[i - 1][j] + arr[i][j];
}
}
ll ans = arr[1][1];
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++)
{
ll sum = 0;
for (int k = 1; k <= m; k++)
{
sum += dp[j][k] - dp[i - 1][k];
if (sum < 0)
sum = 0;
if (ans < sum)
ans = sum;
}
}
cout << ans << endl;
}
return 0;
}