题目描述
给定一个R行C列的矩阵,表示一个矩形网格滑雪场。
矩阵中第 i 行第 j 列的点表示滑雪场的第 i 行第 j 列区域的高度。
一个人从滑雪场中的某个区域内出发,每次可以向上下左右任意一个方向滑动一个单位距离。
当然,一个人能够滑动到某相邻区域的前提是该区域的高度低于自己目前所在区域的高度。
下面给出一个矩阵作为例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
在给定矩阵中,一条可行的滑行轨迹为24-17-2-1。
在给定矩阵中,最长的滑行轨迹为25-24-23-…-3-2-1,沿途共经过25个区域。
现在给定你一个二维矩阵表示滑雪场各区域的高度,请你找出在该滑雪场中能够完成的最长滑雪轨迹,并输出其长度(可经过最大区域数)。
输入格式
第一行包含两个整数R和C。
接下来R行,每行包含C个整数,表示完整的二维矩阵。
输出格式
输出一个整数,表示可完成的最长滑雪长度。
数据范围
1≤R,C≤300,
0≤矩阵中整数≤10000
输入样例:
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
输出样例:
25
//经过评论区网友fzj2007 提醒 修改了接收参数相反的BUG 2020 11 14
算法1
一开始 写的常规DFS
但是在某些数据 超时。果然没那么简单
代码
#include <iostream>
#include <vector>
using namespace std;
const int N = 310;
int n,m;
int maxlen = -1;
vector<vector<int>> vv;
int addx[] = {1,-1,0,0};
int addy[] = {0,0,1,-1};
void dfs(int x,int y,int len)
{
if(len > maxlen) maxlen = len;
for(int i = 0;i < 4;i++){
int newx = x + addx[i];
int newy = y + addy[i];
if(newx >=0 && newx < n && newy >=0 && newy < m &&
vv[newx][newy] < vv[x][y])
{
dfs(newx,newy,len+1);
}
}
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n;i++){
vector<int> v;
vv.push_back(v);
for(int j = 0;j< m;j++){
int t =0;
cin >> t;
vv[i].push_back(t);
}
}
for(int i = 0; i < n;i++){
for(int j = 0;j < m;j++){
int len =0;
dfs(i,j,len);
}
}
cout << maxlen+1 << endl;
return 0;
}
算法2
使用记忆化数组 记录每个点的最大滑动长度
遍历每个点作为起点
然后检测该点四周的点 如果可以滑动到其他的点
那么该点的最大滑动长度 就是其他可以滑到的点的滑动长度+1
dp[i][j] = max(dp[i][j-1], dp[i][j+1],dp[i-1][j],dp[i+1][j])
由于滑雪是必须滑到比当前低的点 所以不会存在一个点多次进入的问题
如果该点的dp[][] 不为初始化值 那么就说明计算过 不必再次计算。
按照上述公式 代码如下 AC
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 310;
int R, C;
int arr[N][N];
int dp[N][N];
int maxlen = -1;
int addx[] = { 1,-1,0,0 };
int addy[] = { 0,0,1,-1 };
int Dfs(int x, int y)
{
if (dp[x][y] != 0) return dp[x][y];
for (int i = 0; i < 4; i++) {
int newx = x + addx[i];
int newy = y + addy[i];
if (newx >= 0 && newx < R && newy >= 0 && newy < C &&
arr[newx][newy] < arr[x][y])
{
dp[x][y] = max(dp[x][y], Dfs(newx, newy) + 1);
}
}
return dp[x][y];
}
int main()
{
ios::sync_with_stdio(false);
cin >> R >> C;
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
cin >> arr[i][j];
}
}
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
int len = Dfs(i, j);
maxlen = max(maxlen, len);
}
}
cout << maxlen + 1 << endl;
return 0;
}
if (a < 1 && a > n && b < 1 && b > m && g[x][y] <= g[a][b]) continue;
佬,判断的时候,用这个来跳过不合法的情况为什么会SF呢?
不存在 a既小于1 有a大于n的情况
这个时候要用 ||
噢噢,对,谢谢
请指正
if (f[x][y] != -1) return f[x][y];//一个点只会被遍历一次,因为从高点进入该点(低点),然后从该点出去需要到比该点更低的点,如果现在到了一个点p可以重新进入该点那么,点p一定要高于该点,但是我们无法从比该点更低的点达到点p
大概意思就是如果f[x][y]这个点被算过的话,就直接返回f[x][y]
其他同学已经回复了。 数组初始化全部诶-1,如果不等于-1,就说明已经被经历过了
这偏移量数组让我回忆起搜索里的bfs比如844走迷宫
大佬,算法1最后一步输出答案的时候为什么要加1呢
选择的边界不同,我计算的是从当前点到下个点,滑动轨迹加1.
题目计算的是从1到25 滑动轨迹是25,所以我的结果总是少1,最后结果加了上去。
也可以在初始化DP的时候 所有DP都赋值为1来进行解决
懂了,感谢大佬!
hack:
正确输出:
您的代码wa了?
C R的定义反了 不知道是题目修改了 还是我之前就写错了
if(newx >=0 && newx < C && newy >=0 && newy < R &&
arr[newx][newy] < arr[x][y])
更换CR 即可
大佬,为什么方案二超时了,我写的和你差不多呀,难道vector ???
#include <iostream> #include <vector> using namespace std; const int N = 310; int R,C; vector<vector<int>> arr(N,vector<int>(N,0)); int dp[N][N]; int maxlen = -1; int addx[] = {1,-1,0,0}; int addy[] = {0,0,1,-1}; int Dfs(int x,int y) { if(dp[x][y] != 0) return dp[x][y]; int pos[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; for(int i = 0; i < 4; i++) { int nx = x + pos[i][0]; int ny = y + pos[i][1]; if(nx < 0 || nx >=R|| ny < 0 || ny >=C || arr[nx][ny] > arr[x][y]) continue; else dp[x][y] = max(Dfs(nx,ny) + 1, dp[x][y]); } return dp[x][y]; } int main() { cin >> R >>C; for(int i = 0; i < R;i++){ for(int j=0;j<C;j++){ cin >> arr[i][j]; } } for(int i = 0; i < R;i++){ for(int j = 0;j< C;j++){ int len = Dfs(i,j); maxlen = max(maxlen,len); } } cout << maxlen +1 << endl; return 0; }
你那个啥错误?
emm 就是Se什么,输出错误
我提交你的代码是内存错误 不是超时。
if(nx < 0 || nx >=R|| ny < 0 || ny >=C
|| arr[nx][ny] > arr[x][y]) <===============大于等于 只有小于 才有继续搜索的必要
continue;