题目描述
Vincenzo 决定制作立方体 IV,但所有预算只够制作一个正方形迷宫。
它是一个完美的迷宫,每个房间都呈正方形,并具有 4 扇门(四个边一边 1 个)。
每个房间里都有一个号码。
一个人只有在下一个房间的号码比当前房间的号码大 1 的情况下,才能从当前房间移动到下一个房间。
现在,Vincenzo 为所有房间分配了唯一的号码(1,2,3,…S2)然后将 S2 个人放在了迷宫中,每个房间 1 个,其中 S 是迷宫的边长。
能够移动次数最多的人将获胜。
弄清楚谁将成为赢家,以及他将能够到达的房间数量。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组测试数据第一行包含整数 S,表示迷宫的边长。
接下来 S 行,每行包含 S 个整数,表示具体的迷宫的房间号分布,需注意 1,2,3,…S2 这 S2 个数字,每个数字只出现一次。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: r d,其中 x 是组别编号(从 1 开始),r 是获胜的人最初所在房间的房间号,d 是他可以到达的房间数量。
如果有多个人可到达的房间数相同,那么最初所在房间的房间号最小的人将获胜。
数据范围
1≤T≤100,
1≤S≤1000
思路
①一开始就想到这道题的数据量会很大,想用搜索来做,但是看了眼闫老师的代码,发现可以用记忆化搜索进行优化。
②记忆化搜索,即搜索过的位置直接返回其值,没搜索过的位置进行搜索。因此在输入矩阵之后,从第一个点开搜,它可以去上、下、左、右,如果找到符合题意的下一个位置,就记录一下当前这个点可以走的最大值。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1005;
int a[maxn][maxn] = {0};
int f[maxn][maxn] = {0};
int s,t;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
int dp(int x,int y){
int& v = f[x][y]; #v为f[x][y]的引用。
if(v != -1) return v; #如果已经被搜过,则直接返回之前搜过的值。
v = 1; #否则初始化为1,因为它再不济也可以走自己(不动)。防止反复搜索。
for(int i=0;i<4;i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx > 0 && ny > 0 && nx <= s && ny <= s && a[nx][ny] == a[x][y] + 1){
v = max(v, dp(nx,ny) + 1);
#动规思想,(nx,ny)是(x,y)下一个可以去的点,由贪心思想,(x,y)能到的房间
#数量一定比它下一个可以到的点(nx,ny)大,因此还需要搜索下一个位置(nx,ny)
#可以到达的房间数((nx,ny)最少是1,故(x,y)最小变成了2)。
}
}
return v;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>t;
for(int cases = 1;cases <= t; cases ++){
cin>>s;
memset(f,-1,sizeof(f)); #每组数据都需要对f初始化
for(int i=1;i<=s;i++){
for(int j=1;j<=s;j++){
cin>>a[i][j];
}
}
int id,cnt = 0;
for(int i=1;i<=s;i++){
for(int j=1;j<=s;j++){
int t = dp(i,j); #从第一给点开始遍历
if(t > cnt || t == cnt && id > a[i][j]){
id = a[i][j];
cnt = t;
}
}
}
cout<<"Case #"<<cases<<": "<<id<<' '<<cnt<<endl;
}
return 0;
}