AcWing 3828. 行走路径行走路径和滑雪 记忆化DFS(注意dfs写法,求最长路)
行走路径 记忆化dfs
给定一个 n×m 的方格矩阵。
每个方格中都包含一个大写字母:Q,W,E,R 之一。
现在,小明要在方格矩阵中进行移动。
具体移动规则如下:
- 最初,小明应选择某个包含字母 Q 的方格作为起点。
- 小明每次移动可以沿上下左右四个方向,移动一格距离。当然不能出界。
- 小明在移动时,必须遵循:从包含字母 Q 的方格只能移动到包含字母 WW的方格,从包含字母 W 的方格只能移动到包含字母 E 的方格,从包含字母 E 的方格只能移动到包含字母 R的方格,从包含字母 RR 的方格只能移动到包含字母 Q 的方格。
- 小明每走过一个完整的
QWER
,就称小明走过了一个轮次。小明需要走过尽可能多的轮次。即统计路径中完整的QWER
的个数,例如WERQWERQ
中仅有1个个QWER
。
请问小明最多可以走过多少轮次?
输入格式
第一行包含两个整数 n,m。
接下来 n 行,每行包含 mm个字符,每个字符为 Q,W,E,R 之一。
输出格式
如果小明无法成功走过任何轮次,则输出 none
。
如果小明可以成功走过无穷多轮次,则输出 infinity
。
如果小明可以成功走过有限多轮次,则输出他能走过的最多轮次。
数据范围
前六个测试点满足 1≤n,m≤51≤n,m≤5。
所有测试点满足 1≤n,m≤10001≤n,m≤1000。
输入样例1:
1 2
QW
输出样例1:
none
输入样例2:
2 2
ER
WQ
输出样例2:
infinity
输入样例3:
5 5
QWERQ
QWERW
QWERE
QQERR
RREWQ
输出样例3:
4
题解
个人解法
infinity:判断一下是否能走回自己走过的路即可,即判断环。
记忆化dfs,关键是dfs过程保存num[x,y](表示沿着x,y向下走最多有多少QWER(包括x,y所在的QWER)),按照自己的思路在dfs回溯的过程中构造了rsum, 具体看代码即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
typedef long long LL;
int n, m;
int a[N][N], st[N][N], num[N][N];
int sum = 0, res = 0, flag = 0, ans = 0, rsum = 0;
int dx[] = {0, 0, -1, 1};
int dy[] = {1, -1, 0, 0};
int dfs(int x, int y)
{
if(flag == -1) return;
if(num[x][y] != 0)
{
res = max(res, sum + num[x][y] - 1);
return ;
}
for(int i = 0; i < 4; i++)
{
int x1 = x + dx[i], y1 = y + dy[i];
if(a[x1][y1] != -1 && (a[x1][y1] - a[x][y] == 1 || a[x1][y1] - a[x][y] == -3))
{
if(st[x1][y1] == 1)
{
flag = -1;
return;
}
if(a[x1][y1] == 3)
{
sum++;
// 遇到新路需要置为0,保证rsum从最底层开始累加
rsum = 0;
res = max(sum, res);
}
st[x1][y1] = 1;
dfs(x1,y1);
if(a[x1][y1] == 3)
{
sum--;
rsum++;
}
num[x1][y1] = max(rsum, num[x1][y1]);
st[x1][y1] = 0;
}
}
}
int main()
{
memset(a, -1, sizeof(a));
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++)
{
string s;
cin>>s;
for(int j = 0; j < m; j ++)
{
if(s[j] == 'W')
{
a[i + 1][j + 1] = 1;
}
else if(s[j] == 'E')
{
a[i + 1][j + 1] = 2;
}
else if(s[j] == 'R')
{
a[i + 1][j + 1] = 3;
}
else if(s[j] == 'Q')
{
a[i + 1][j + 1] = 0;
}
}
}
for(int i = 1; i<= n; i++)
{
for(int j = 1; j<=m; j++)
{
if(a[i][j] == 0 )
{
st[i][j] = 1;
dfs(i, j);
if(flag == -1)
{
break;
}
sum=0;
st[i][j] = 0;
num[i][j] = max(num[i][j], rsum);
rsum = 0;
}
}
/*
cout<<"res:"<<res<<endl;
cout<<endl;
for(int x = 1; x <= n; x++)
{
for(int y = 1; y <= n; y++)
{
cout<<num[x][y]<<" ";
}
cout<<endl;
}*/
if(flag == -1)
{
break;
}
}
if(flag == -1)
{
printf("infinity\n");
}
else if(res == 0)
{
printf("none\n");
}
else
{
printf("%d\n",res);
}
return 0;
}
但上述代码有点麻烦,简单逻辑看一下下方的滑雪题目即可,这题就简单加了一个判断环。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
char g[N][N];
bool st[N][N];
int f[N][N];
bool is_inf;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dp(int x, int y)
{
if (is_inf) return -1;
int& v = f[x][y];
if (v != -1) return v;
st[x][y] = true;
v = 1;
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < n && b >= 0 && b < m && g[a][b] == (g[x][y] + 1) % 4)
{
if (st[a][b])
{
is_inf = true;
return -1;
}
v = max(v, dp(a, b) + 1);
}
}
st[x][y] = false;
return v;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
scanf("%s", g[i]);
for (int j = 0; j < m; j ++ )
{
auto& c = g[i][j];
if (c == 'Q') c = 0;
else if (c == 'W') c = 1;
else if (c == 'E') c = 2;
else c = 3;
}
}
memset(f, -1, sizeof f);
int res = 0;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
{
int t = dp(i, j);
if (g[i][j]) t -= 4 - g[i][j];
res = max(res, t / 4);
}
if (is_inf) puts("infinity");
else if (!res) puts("none");
else printf("%d\n", res);
return 0;
}
推荐题目:滑雪
给定一个 R行 C列的矩阵,表示一个矩形网格滑雪场。
矩阵中第 i 行第 j列的点表示滑雪场的第 i 行第 jj列区域的高度。
一个人从滑雪场中的某个区域内出发,每次可以向上下左右任意一个方向滑动一个单位距离。
当然,一个人能够滑动到某相邻区域的前提是该区域的高度低于自己目前所在区域的高度。
下面给出一个矩阵作为例子:
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≤3001≤R,C≤300,
0≤矩阵中整数≤100000≤矩阵中整数≤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
题解
代码中st用不到,熟悉一下这种dfs形式
#include<bits/stdc++.h>
using namespace std;
const int N = 310;
int n,m,g[N][N];
int f[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dp(int x, int y){
if(f[x][y] != -1){
return f[x][y];
}
st[x][y] = true;
int &v = f[x][y];
v = 1;
for (int i = 0; i < 4; i ++ ){
int a = x + dx[i], b = y + dy[i];
if(a >= 0 && a < n && b >=0 && b < m && g[a][b] < g[x][y]){
v = max(v, dp(a, b) + 1);
}
}
st[x][y] = false;
return v;
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++){
for(int j = 0; j <m; j++){
scanf("%d", &g[i][j]);
}
}
memset(f, -1, sizeof(f));
int res = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
res = max(res, dp(i, j));
}
}
cout<<res<<endl;
return 0;
}