题目
Hello Kitty想摘点花生送给她喜欢的米老鼠。
她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。
地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。
Hello Kitty只能向东或向南走,不能向西或向北走。
问Hello Kitty最多能够摘到多少颗花生。
输入格式
第一行是一个整数T,代表一共有多少组数据。
接下来是T组数据。
每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。
每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。
输出格式
对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。
数据范围
1≤T≤100,
1≤R,C≤100,
0≤M≤1000
输入样例:
2
2 2
1 1
3 4
2 3
2 3 4
1 6 5
输出样例:
8
16
思路1
本题涉及到最优解,考虑使用DP(动态规划)来做。对于动态规划的题,从状态表示和状态计算两方面考虑。
对于状态表示,又可以从集合定义和集合的属性两方面考虑。对于本题,我将集合f(i, j)定义为从某坐标(i, j)出发的所有路径,属性(即f(i, j)存的值)定义为摘到花生的最大数量。
而状态计算对应着集合的划分。对于本题,可以按第一步往哪个方向走来划分集合,分为两个小的集合:第一步向东走的,第一步向南走的。假设用二维数组a存每个坐标的花生数量。对于第一步向东走的集合,路径为:(1, 1) ->(1, 2) ->……,第一步都是在(1, 1)这个坐标,那摘到的花生数可以设为f(1, 1) = m(1, 1) + f(1, 2)。同理,对于第一步向南走的集合,路径为:(1, 1) ->(2, 1) ->……,第一步都是在(1, 1)这个坐标,那摘到的花生数可以设为f(1, 1) = m(1, 1) + f(2, 1)。然后两个小集合的最大值即可得到整个集合的最大值。
有人疑惑为什么这样可以摘到从西北角到东南角最大的花生数量。因为对于某个坐标,我们都会遍历东南两个方向,因此一定会到达东南角的。
代码1
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int t, r, c, m;
//a存各坐标花生数;total[i][j]存从(i, j)开始能获得的最大花生数量,为记忆化数组
int a[N][N], total[N][N];
int dx[] = {0, 1}, dy[] = {1, 0};
int dfs(int x, int y)
{
int &v = total[x][y];
/*
对total[x][y]的各位取反,判断total[x][y]是否为-1,-1(1111 1111 1111 1111)取反后变为为0;
不为-1说明从(x, y)开始的路径已经遍历(记忆)过了,不需要重新遍历(记忆)
*/
if (~v)
return v;
v = a[x][y];
for (int i = 0; i < 2; i ++)
{
int nx = dx[i] + x, ny = dy[i] + y;
//合理的坐标才能继续摘花生
if (nx >= 1 && nx <= r && ny >= 1 && ny <= c)
v = max(v, dfs(nx, ny) + a[x][y]);
}
//遍历完两个方向后将结果v返回给上一层
return v;
}
int main()
{
cin >> t;
while (t --)
{
/*
因为某个坐标的花生数可能为0,因此要使用-1代表total[i][j]没有计算过从(i, j)开始能获得的最大花 生数量
*/
memset(total, -1, sizeof total);
cin >> r >> c;
for (int i = 1; i <= r; i ++)
for (int j = 1; j <= c; j ++)
cin >> a[i][j];
cout << dfs(1, 1) << endl;
}
return 0;
}
思路2
由于Hello Kitty只能向东或向南走,因此Hello Kitty到达某点坐标只能从西边或者北边来。那么我们可以将
- 集合f(i, j)定义为:到达(i, j)的所有路径。
- 属性:获得花生的最大数量。
- 集合划分:从西边到达(i, j); 从北边到达(i, j)
f(i, j) = a[i][j] + f(i, j - 1);
f(i, j) = a[i][j] + f(i - 1, j);
代码2
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
//a存各坐标花生数;f[i][j]存到达(i, j)能获得的最大花生数量
int a[N][N], f[N][N];
int main()
{
int t, r, c;
cin >> t;
while (t --)
{
//由于有多组不同的数据,因此每次都要重置为0
memset(f, 0, sizeof f);
cin >> r >> c;
for (int i = 1; i <= r; i ++)
for (int j = 1; j <= c; j ++)
cin >> a[i][j];
for (int i = 1; i <= r; i ++)
{
for (int j = 1; j <= c; j ++)
{
f[i][j] = max(f[i][j - 1], f[i - 1][j]) + a[i][j];
}
}
cout << f[r][c] << endl;
}
return 0;
}