从本篇开始,准备做一系列的专题讲解,主要参考《算法竞赛入门经典》、《算法竞赛进阶指南》两本书。主要是为了能够更加系统的讲解各个知识点,这两本书已经讲得很好了,建议准备ACM学习以及想深入学习算法的同学购买。
每一个专题都会持续比较久的时间,就比如拿动态规划DP来说,种类非常多,从浅入深可以说是一次深潜,刚开始可能还好,后面会比较困难。限于本人水平有限,我能潜多深,就带大家看多深的风光
~
另外本号的更新频率一般是一周2-3篇,关注本号的童鞋请耐心一点哈。
来看题:
The Triangle
Description
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
(Figure 1)
Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.
Input
Your program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but <= 100. The numbers in the triangle, all integers, are between 0 and 99.
Output
Your program is to write to standard output. The highest sum is written as an integer.
Sample Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30
这是DP的入门经典题目,思路的关键是针对位置[i][j]进行状态转移,dp[i][j]表示从第一行走到第i行第j列的最大和。
动态规划里面有很多问题的dp都是以位置i开始或者结束,比如下一篇要讲得LIS问题,不过本题是一个二维的表示。
本题用递推的方式就已经很好实现了,建议不熟的同学再尝试递归的方法。
状态转移方程:
dp[i][j] = A[i][j] + max(dp[i-1][j], dp[i-1][j-1])
A[i][j]是当前位置的值,加上上一行相邻的两个值的较大值,保证了最优子结构。
注意j-1小于0的特殊处理。
时间复杂度:dp的状态总数*决策复杂度,这里是二维的,状态总数也就是i*j,决策只有2中决策。总复杂度O(n2)。
进阶的书里面和本题解法一致,算法竞赛入门里面是从d[i+1][j+1]到d[i][j],思路恰好是逆序的,都可以看看。
最重要的是找到转移方程和边界。
源代码:G++
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <set>
#include <vector>
#include <string>
#include <string.h>
using namespace std;
#ifdef __MSYS__
#define printfd printf
#else
#define printfd
#endif
int main() {
#ifdef __MSYS__
freopen("test.txt", "r", stdin);
#endif
int dp[100][100] = {0};
int a[100][100] = {0};
int n;
scanf("%d", &n);
for(int i=0; i<n; ++i){
for(int j=0; j<i+1; ++j){
scanf("%d", &a[i][j]);
}
}
dp[0][0] = a[0][0];
for(int i=1; i<n; ++i){
for(int j=0; j<i+1; ++j){
if(j-1>=0){
dp[i][j] = a[i][j] + std::max(dp[i-1][j], dp[i-1][j-1]);
}else{
dp[i][j] = a[i][j] + dp[i-1][j];
}
printfd("%d %d %d\n", i, j, dp[i][j]);
}
}
int m=0;
for(int j=0; j<n; ++j){
m = std::max(dp[n-1][j], m);
}
printf("%d\n", m);
return 0;
}
PS~这里我用宏控制了printfd,只有在我的msys环境下才会生效,这样就可以不用改代码直接提交poj评测系统了。
温馨提示
如果你喜欢本文,请分享到朋友圈,想要获得更多信息,请关注ACM算法日常。