算法训练:动态规划

数字三角形

时间限制: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 }

动态规划自上而下,一行一行求解

 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 }
View Code

自下而上,一行一行求解,所有下标均从 0 开始,data[0][0] 代表第1行第1列数据,代码如下

 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 }
View Code

 

背包问题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。

代码实现

 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 }
View Code

 

posted on 2019-01-12 17:01  flysong  阅读(209)  评论(0编辑  收藏  举报

导航