算法训练:动态规划
数字三角形
时间限制:2 sec
空间限制:256 MB
问题描述
给定一个高度为 n 的“数字三角形”,其中第 i 行(1<=i<=n)有 i 个数。(例子如下图所示)
初始时,你站在“数字三角形”的顶部,即第一行的唯一一个数上。每次移动,你可以选择移动到当前位置正下方或者当前位置右下方的位置上。即如果你在 (i,j)(表示你在第i行从左往右数第j个数上,下同),你可以选择移动到 (i+1,j) 或 (i+1,j+1)。
你想让你经过的所有位置(包括起点和终点)的数字总和最大。求这个最大值。
输入格式
第一行一个正整数 n,表示数字三角形的大小。
第 2 行到第 n+1 行,第 i+1 行为 i 个用空格隔开的非负整数,描述数字三角形的第 i 行。
输出格式
一行一个整数,表示经过路径上数的最大总和。
样例输入
4
1
2 3
4 5 6
7 8 9 10
样例输出
20
样例解释
不停地向右下走即可。
数据范围
对于 50% 的数据,保证 n<=5。
对于 100% 的数据,保证 n<=1,000,保证数字三角形内的数不超过 10^6。
提示
[如果我们使用搜索算法,我们会在搜索时记录哪些信息呢?]
[当前到达的点的坐标、当前经过路径上数的总和!]
[总和显然是越大越好!]
[于是可以设计出状态:dp[i][j] 表示走到坐标为 (i,j) 的点时的最大总和。]
[很容易就可以设计出状态转移方程啦!]
代码实现
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 // ================= 代码实现开始 ================= 5 6 /* 请在这里定义你需要的全局变量 */ 7 8 // 本函数计算答案(最大经过位置数字总和) 9 // n:描述数字三角形大小,意义同题目描述 10 // a:描述整个数字三角形,第 i 行的第 j 个数存放在 a[i][j] 11 // 中(你需要特别注意的是,所有下标均从 1 开始,也就是说下标为 0 的位置将存放无效信息) 12 // 返回值:最大经过位置数字总和 13 int getAnswer(int n, vector<vector<int>> a) { 14 /* 请在这里设计你的算法 */ 15 } 16 17 // ================= 代码实现结束 ================= 18 19 int main() { 20 int n; 21 vector<vector<int>> a; 22 scanf("%d", &n); 23 a.resize(n + 1); 24 for (int i = 0; i <= n; ++i) 25 a[i].resize(i + 1); 26 for (int i = 1; i <= n; ++i) 27 for (int j = 1; j <= i; ++j) 28 scanf("%d", &a[i][j]); 29 int ans = getAnswer(n, a); 30 printf("%d\n", ans); 31 return 0; 32 }
动态规划自上而下,一行一行求解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <vector> 2 #include <iostream> 3 #include <algorithm> 4 5 using namespace std; 6 7 vector<vector<int>> dp; 8 9 int getAnswer(int n, vector<vector<int>> a) 10 { 11 dp.resize(n + 1); 12 int ans = 0; 13 for (int i = 0; i <= n; i++) 14 { 15 dp[i].resize(i + 2); 16 } 17 18 for (int i = 1; i <= n; i++) 19 for (int j = 1; j <= i; j++) 20 dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j]; 21 22 23 for (int i = 1; i <= n; ++i) 24 ans = max(ans, dp[n][i]); 25 26 return ans; 27 }
自下而上,一行一行求解,所有下标均从 0 开始,data[0][0] 代表第1行第1列数据,代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <algorithm> 2 #include <vector> 3 #include <iostream> 4 5 using namespace std; 6 7 int n; 8 vector<int> data[1005]; 9 int dp[1005]; 10 int main_dsa() 11 { 12 int x; 13 scanf("%d", &n); 14 15 //数据输入 16 for (int i = 0; i < n; ++i) 17 { 18 for (int j = 0; j < i + 1; ++j) 19 { 20 scanf("%d", &x); 21 data[i].push_back(x); 22 } 23 } 24 25 // 最后一行数据 26 for (int i = 0; i < n; ++i) 27 { 28 dp[i] = data[n - 1][i]; 29 } 30 31 for (int i = n - 2; i >= 0; --i) 32 for (int j = 0; j < i + 1; ++j) 33 dp[j] = data[i][j] + max(dp[j], dp[j + 1]); 34 35 printf("%d\n", dp[0]); 36 return 0; 37 }
背包问题1
描述
n种物品,每种物品有相应的价值和体积,同时物品还分为两类,一类是“单个物品”,即该种物品只有一个;一类是“多个物品”,即该种物品有无限个。
现在你有一个体积为V的背包,那么该装些什么物品到背包里使得价值之和最大呢?
输入
第一行包含两个正整数n,V。
接下来n行,每行代表一种物品。每行的第一个数字表示该物品的种类(若为0表示“单个物品”,若为1表示“多个物品”),第二个数字表示该物品的价值,第三个数字表示该物品的体积。
输出
输出一个整数,表示最大的价值之和。
样例1输入
5 8
0 6 8
0 7 3
1 1 1
0 8 1
0 5 2
样例1输出
22
样例1解释
第三种物品有无限个,其余都是单个物品。
若我们放入物品1,则背包已经装满,此时价值和为6;
若我们放入物品2、4、5,背包所剩体积为8-3-1-2=2,此时价值和为7+8+5=20;
若我们放入8个物品3,背包装满,此时价值之和为8×1=8;
若我们放入物品2、4、5,再放两个物品3,则背包装满,此时价值和为7+8+5+2×1=22。
可以验证,最优答案就是22。
限制
对于30%的数据,n,V ≤ 20;
对于100%的数据,n,V ≤ 5000。
保证数据中所有的整数均为正整数,且不超过5000。
代码实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // ================= 代码实现开始 ================= 2 3 /* 请在这里定义你需要的全局变量 */ 4 5 // n:物品个数 6 // V:背包的体积 7 // type:长度为n的数组,第i个元素若为0,表示物品i为单个物品;若为1,表示物品i为多个物品。(i下标从0开始,下面同理) 8 // w:长度为n的数组,第i个元素表示第i个物品的价值 9 // v:长度为n的数组,第i个元素表示第i个物品的体积 10 // 返回值:最大价值之和 11 int getAnswer(int n, int V, vector<int> type, vector<int> w, vector<int> v); 12 13 // ================= 代码实现结束 ================= 14 15 int main() { 16 int n, V; 17 scanf("%d%d", &n, &V); 18 vector<int> T, W, _V; 19 for (int i = 0; i < n; ++i) { 20 int t, w, v; 21 scanf("%d%d%d", &t, &w, &v); 22 T.push_back(t); 23 W.push_back(w); 24 _V.push_back(v); 25 } 26 printf("%d\n", getAnswer(n, V, T, W, _V)); 27 return 0; 28 } 29 //--------------------------------- 30 int getAnswer(int n, int V, vector<int> type, vector<int> w, vector<int> v) 31 { 32 for(int i = 0; i < n; i++) 33 if (type[i])//完全背包,顺序枚举 34 for(int j = V; j >= v[i]; j--) 35 f[j] = max(f[j],f[(j-v[i])] + w[i]); 36 else //0-1背包 37 for(int j = v[i]; j <= V; j++) 38 f[j] = max(f[j],f[(j-v[i])] + w[i]); 39 return f[V]; 40 }