题目
题目描述:帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:
1.每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 * 2i,其中i表示第i次取数(从1开始编号);
4.游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入描述:
第1行为两个用空格隔开的整数n和m。
第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。
输出一个整数,即输入矩阵取数后的最大得分。
解析
1)知识点
- 这道题其实也没用到啥,可以看作就是一个没有那么简单的递归题。
- dfs遍历就好了,为了加快速度,我们要用记忆化搜索(备忘录法)。
- 这道题很大,需要用到高精度,但是我们可以用__int128简单的搞。
2)看题目
- 我们首先看题目说,我们每次要从两头选一个数字进行操作。
- 所以我们就简单的对两头选最大的就好了,就暴力的dfs遍历(从数据范围小也可以看出来)。
3)算法分析
- 这道题要说算法其实也没什么算法,因为高精度可以用__int128代替,其他就是暴力了。
- 我们知道,因为每一行是独立的,所以可以求出每一行的大小再加起来啦。
- 简单的说一下怎么暴力吧:
- 我们就是递归的算出左边和右边的值。
- 然后选取出两边最后求和比较大的一组返回就行了。
- 这里我们用一个dp数组对区间的最大值进行保存。用于减少相同的区间的大量重复计算。
4)算法操作
- 那我们先看一下怎么暴力,首先是递归计算左右的值:
big left = ((big)1 << move) * info[l] + dfs(l + 1, r);//选取左边的值 big right = ((big)1 << move) * info[r] + dfs(l, r - 1);//选取右边的值
这里的题目要求是乘上2^i,所以我们可以直接用移位运算符来计算,同时计算也会快很多。
注意move的算法:int move = l - 1 + m - r + 1;//左移,左边删除的位数+右边删除的位数,相当于乘上2^i
- 然后我们看一下返回的情况:
return dp[l][r] = max(left, right);//返回最大值用于累加出最大值
- 同时我们还要注意递归结束的条件:
if (l > r) return 0;
- 这里再特判一下,如果备忘录已经几率过这个值的情况(就可以直接返回这个记录值了):
if (dp[l][r]) return dp[l][r];
5)打代码
- 打代码咯~
- 所以其实就是先输入行列值。
- 然后一行一行输入呗。
- 每一行进行一个dfs然后加起来。
- 看我代码咯~
AC代码
#include <iostream> #include <algorithm> #include <cstring> #include <string> using namespace std; typedef __int128 big; #define IOS ios::sync_with_stdio(false); const int MAX = 87; int n, m, info[MAX];//矩阵的长和宽 big dp[MAX][MAX];//行数组,备忘录数组 template<class T> inline void read(T& res); template<class T> inline void write(T res); big dfs(int l, int r); int main() { read(n); read(m); big ans = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) read(info[j]); memset(dp, 0, sizeof dp); ans += dfs(1, m); } write(ans); putchar(10); return 0; } template<class T> inline void read(T& res) { char c; int flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } template<class T> inline void write(T res) { if (res < 0) { putchar('-'); res = -res; } if (res > 9) write(res / 10); putchar(res % 10 + '0'); } big dfs(int l, int r) { if (l > r) return 0; if (dp[l][r]) return dp[l][r]; int move = l - 1 + m - r + 1;//左移,左边删除的位数+右边删除的位数,相当于乘上2^i big left = ((big)1 << move) * info[l] + dfs(l + 1, r);//选取左边的值 big right = ((big)1 << move) * info[r] + dfs(l, r - 1);//选取右边的值 return dp[l][r] = max(left, right);//返回最大值用于累加出最大值 }