原题链接
P1434
题目类型:
普
及
+
/
提
高
{\color{yellow}{普及+/提高}}
普及+/提高
AC记录:
题目大意
给你一个 n × m n\times m n×m的矩阵 a a a, a i , j a_{i,j} ai,j代表 ( i , j ) (i,j) (i,j)这个地方的高度,你可以从任意一个地方出发,然后走到一个和这个地方四联通并且高度严格小于当前位置高度地方,求你可以走的最长路线长度。
输入格式
输入的第一行为表示区域的二维数组的行数 n n n和列数 m m m。下面是 n n n行,每行有 m m m个数,代表高度(两个数字之间用 1 1 1个空格间隔)。
输出格式
输出区域中你可以走的最长路线长度。
S
a
m
p
l
e
\mathbf{Sample}
Sample
I
n
p
u
t
\mathbf{Input}
Input
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
S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output
25
H
i
n
t
&
E
x
p
l
a
i
n
\mathbf{Hint\&Explain}
Hint&Explain
直接从
(
3
,
3
)
(3,3)
(3,3)开始然后螺旋走,途径高度为25-24-23-22-21-...-3-2-1
。
数据范围
对于 100 % 100\% 100%的数据, 1 ≤ n , m ≤ 100 1\le n,m\le 100 1≤n,m≤100。
解题思路
这题可以用两种方法来做。
①记忆化搜索
如果直接使用搜索,按照四联通的方法走的话,绝对会超时。因此我们要使用记搜。
在每一次经过一个位置的时候,记录一个
f
f
f数组,表示当前最长的滑雪路径长度,这样每一个位置最多只用执行一次操作,大大减少了原来的时间复杂度。
核心代码:
int dfs(int x,int y)
{
if(f[x][y])
return f[x][y];
f[x][y]=1;
for(int i=1; i<=4; i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&a[xx][yy]<a[x][y])
f[x][y]=max(f[x][y],dfs(xx,yy)+1);
}
return f[x][y];
}
注意事项
1.在判断方向时,一定要注意不要走错方向!这里给出一个记忆方法:
对于四联通来说,只要 x x x坐标加一减一, y y y坐标也加一减一就可以了
int dx[5]={0,-1,1,0,0};
int dy[5]={0,0,0,-1,1};
对于八连通来说,只要 x x x坐标三个连续的
-1
和三个连续的0
和三个连续的1
, y y y坐标连续三个-1,0,1
就可以了。这里把(0,0)
提前了。
int dx[9]={0,-1,-1,-1,0,0,1,1,1};
int dy[9]={0,-1,0,1,-1,1,-1,0,1};
②
d
p
dp
dp做法
由于滑雪的路程是从高到低,所以只要你到达了一个点,就不会返回你来的那个点,满足
d
p
dp
dp的无后效性,所以本题可以用
d
p
dp
dp解决。
既然要满足无后效性,那么我们肯定要对整个地图进行排序,记录之前的位置和高度,在按照高度从大到小排序。作者这里用到的是STL里面的priority_queue
,如果用了priority_queue
,记得要重载()
运算符!
排完序后,就可以进行
d
p
dp
dp了。枚举每一个高度,搜索比他小的四联通的高度,由于我们先前已经排好序了,所以会快很多。枚举到一个与他相邻的格子,就要注意进行状态转移了,直接在当前长度上加一即可。
但是最后的答案是
max
1
≤
i
≤
n
×
m
f
i
\max_{1\le i\le n\times m}f_i
max1≤i≤n×mfi!
如果说按照最高的高度可以到达更多个点的话,那么下面这个数据点就过不了:
5 5
2 3 4 5 6
17 1 1 1 7
16 1 25 1 8
15 1 1 1 9
14 13 12 11 10
在这个数据点中,
f
25
f_{25}
f25的值为2
,而标准答案是16
!
所以说要取
f
f
f数组里面所有数的最大值。
注意事项:
1.使用
priority_queue
时要加上头文件#include<queue>
,这里的使用方法为priority_queue<obj,vector<obj>,cmp> pq;
其中的cmp
为单独的一个用来储存()
重载的结构体。
2.也是一样,在判断方向的时候一定不要算错!
最后,祝大家早日
上代码
d f s dfs dfs算法
#include<iostream>
using namespace std;
int n,m;
int a[1010][1010];
int f[1010][1010];
int dx[5]={0,0,0,-1,1};
int dy[5]={0,-1,1,0,0};
int dfs(int x,int y)
{
if(f[x][y])
return f[x][y];
f[x][y]=1;
for(int i=1; i<=4; i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=m&&a[xx][yy]<a[x][y])
f[x][y]=max(f[x][y],dfs(xx,yy)+1);
}
return f[x][y];
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>a[i][j];
int sum=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
sum=max(sum,dfs(i,j));
// for(int i=1; i<=n; i++)
// {
// for(int j=1; j<=m; j++)
// cout<<f[i][j]<<" ";
// cout<<endl;
// }
cout<<sum<<endl;
return 0;
}
d p dp dp算法
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct obj{
obj(int a=0,int b=0,int c=0):x(a),y(b),val(c){}
int x,y,val;
};
struct cmp{
inline bool operator()(obj,obj);
};
inline bool cmp::operator()(obj x,obj y)
{
return x.val>y.val;
}
priority_queue<obj,vector<obj>,cmp> pq;
int n,m,tar;
int dx[5]={0,0,0,-1,1};
int dy[5]={0,-1,1,0,0};
int a[110][110];
int f[110][110];
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>a[i][j],pq.push(obj(i,j,a[i][j]));
memset(f,-0x7f7f7f7f,sizeof(f));
while(pq.size())
{
obj now=pq.top();
pq.pop();
int x=now.x,y=now.y,val=now.val;
f[x][y]=1;
for(int i=1; i<=4; i++)
{
int xx=x+dx[i],yy=y+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=n&&a[xx][yy]<a[x][y])
f[x][y]=max(f[x][y],f[xx][yy]+1);
}
tar=max(tar,f[x][y]);
}
cout<<tar<<endl;
return 0;
}
完美切题 ∼ \sim ∼