擀面皮
有一块1x1的方形面团(不考虑面团的厚度),其口感值为0。擀面师傅要将其擀成一个N x M(纵向长N,横向宽M)的面皮。师傅的擀面手法娴熟,每次下手,要么横向擀一下(使得横向长度增加1),要么纵向擀一下(使得纵向长度增加1)。此外,当面团(皮)的大小为a x b时,往横向擀一下会使得面的口感值上升H_ab,而往纵向擀一下则会使口感值上升V_ab。
现在,请你来将1x1的面团擀成N x M面皮。显然,从1x1的面团擀成N x M的面皮有多种不同的操作序列可以实现,不同操作序列下得到的最终面皮口感值也可能是不同的。请问最终得到的N x M面皮,口感值最高可为多少?
输入描述
第一行两个整数N,M,表示要擀出来面皮的大小(纵向长N,横向宽M)。
接下来有N行,每行M个数。第a行第b列的数值H_ab,表示当面皮大小为a x b时,横向擀一下后,面皮口感的上升值。
再接下来有N行,每行M个数。第a行第b列的数值V_ab,表示当面皮大小为a x b时,纵向擀一下后,面皮口感的上升值。
(0 < N, M < 1000,0 <= H_ab, V_ab <= 1000)
输出描述
输出最终得到的N x M面皮的最高的口感值。
示例1:
输入:2 3
1 2 3
4 5 6
11 12 13
14 15 16
输出:20
示例2:
输入:3 3
1 0 2
2 0 2
2 2 0
0 2 2
1 2 1
2 1 2
输出:7
提示
【示例1解释】
一共三种擀面方法:
纵横横:11+4+5=20
横纵横:1+12+5=18
横横纵:1+2+13=16
【示例2解释】
最优擀面方法为:横(1) + 纵(2) + 纵(2) + 横(2) = 7
限制
时间:1000ms
空间:512MB
#include <iostream>
#include <vector>int main() {int m, n;std::vector<std::vector<int>> hSave;std::vector<std::vector<int>> vSave;std::cin>>m>>n;int result[m][n];for (int i = 0; i < m; ++i) {std::vector<int> temp(n);for (int j = 0; j < n; ++j) {std::cin>>temp[j];}hSave.push_back(temp);}for (int i = 0; i < m; ++i) {std::vector<int> temp(n);for (int j = 0; j < n; ++j) {std::cin>>temp[j];}vSave.push_back(temp);}result[0][0] = 0;for (int i = 1; i < m; ++i) {result[i][0] = result[i - 1][0] + vSave[i - 1][0];}for (int i = 1; i < n; ++i) {result[0][i] = result[0][i - 1] + hSave[0][i - 1];}for (int i = 1; i < m; ++i) {for (int j = 1; j < n; ++j) {result[i][j] = std::max(result[i - 1][j] + vSave[i - 1][j],result[i][j - 1] + hSave[i][j - 1]);}}std::cout<<result[m - 1][n - 1]<<std::endl;return 0;
}
解题算法:动态规划
解题思路:将擀面皮题转化为过门得分的思路,假设是m*n个房间,以m行n列的方式摆放在一起,从左上角(0, 0)出发,到右下角(m - 1, n - 1),且只能向右或向下,需要经过m - 1 + n - 1道门,因为不能通向外界,所以通向外界门的分数也就无用了,如下图
可见,纵向最下与横向最右,均为无法使用的值,所以我们只需要考虑可到达其他房间的门的数据。
我们知道到达每个房间时的得分,与上方的房间加门和左侧房间加门相关,取两者最大值,作为当前房间的得分。最左侧的每间房得分只与上方有关,顶层的每间房得分只与左侧有关,所以我们可以先得到顶层和最左侧的房间得分。
假设我们的表格名为result,横向数据用 h 表示,纵向数据用 v 表示我们的转移方程如下
因为顶层与最左侧已经填充完毕,所以不用担心下标为负值的情况。