30
dp经典题
类似一个树状结构,dp状态定义为到当前节点(包括当前节点)最大和,子问题是到左右子节点(包括节点本身)最大和,状态转移方程可描述为
取到左右子节点为止(包括节点本身)最大和中的的较大值加上当前节点的值即为到当前节点(包括当前节点)最大和
dp[i][j] = max( dp[i + 1][j], dp[i + 1][j + 1] ) + a[i][j]
可能最容易想到的方法是直接递归,利用递归本身回溯特性来从低到上计算,但是如果不做什么处理的话可能超时
#include <cstdio> #include <algorithm> #include <iostream> using namespace std; int a[105][105]; int n; int solve(int i, int j) { //容易超时,因为很多子问题被重复计算了多次 if (i == n) { return a[i][j]; } else { return a[i][j] + max(solve(i + 1, j), solve(i + 1, j + 1)); } } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { scanf("%d", &a[i][j]); } } printf("%d\n", solve(1, 1)); return 0; }
#include <cstdio> #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> using namespace std; int a[105][105], dp[105][105], n;; int sized = sizeof(dp); int solve(int i, int j) { if (dp[i][j] >= 0) return dp[i][j]; //说明之前计算过 //没有计算过 if (i == n) { //边界 dp[i][j] = a[i][j]; } else { dp[i][j] = a[i][j] + max(solve(i + 1, j + 1), solve(i + 1, j)); } return dp[i][j]; } int main() { scanf("%d", &n); memset(dp, -1, sized); for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { scanf("%d", &a[i][j]); } } printf("%d\n", solve(1, 1)); return 0; }
#include <cstdio> #include <algorithm> #include <iostream> using namespace std; int a[105][105]; int dp[105][1005]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { scanf("%d", &a[i][j]); } } for (int i = 1; i <= n; i++) dp[n][i] = a[n][i]; for (int i = n - 1; i >= 1; i--) { for (int j = 1; j <= n; j++) { dp[i][j] = a[i][j] + max(dp[i + 1][j + 1], dp[i + 1][j]); } } printf("%d\n", dp[1][1]); return 0; }