洛谷的题解有点千篇一律了,都是二分,原因也都是n,m<=1e6,但是原题有一个约数条件:n*m<=1e6,所以我们可以动态开数组,而不是固定的。
tip:函数和lambda表达式或者function无法调用第二维没有确定的数组,如int a[n+1][m+1],此时就不要用函数了,直接把函数的式子拿出来用。
先来看题目。
第一反应可能是动态规划,因为只能往下走和往右走,但是这道题思考之后发现没有什么关联。
{1,2,4,5}输出的是mex(S)=0,根据下面的样例也可以发现,mex的值由最小值决定。
某一条路径获取到的最小值从0开始往上,获取的越多,mex才有可能越大。
如{0,1,2,3,4}的话,mex就是5了。
所以根据思路,我们一定要走到0上。
稍加分析,显然这个0不管在哪里都会被我们走到,所以任意设一个点。
因为只能往右和往下走,显然0的右上角是不能再有其他数被经过了,以及左下角也是。
那么0之后的1,2,3,4,如果要合法,必然要出现在没有叉的地方,假设1在左上角。
同理的,1的右上角和左下角也都不可取了。
根据打叉的方式可以发现,我们最多遍历N*M个点,即可以使用bool vis[n+1][m+1]的方式来通过此题。
但是记得要在获取n和m之后再开数组,提前开好范围的话这道题内存就不够了。
#include <bits/stdc++.h>
//#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define fr first
#define se second
#define endl '\n'
using namespace std;
void solve(){
int n,m;
cin>>n>>m;
struct{
int x,y;
}g[n*m];
per(i,1,n){
per(j,1,m){
int tmp;
cin>>tmp;
g[tmp]={i,j};
}
}
bool vis[n+1][m+1];
per(i,1,n){
per(j,1,m){
vis[i][j]=false;
}
}
int p=-1;
while(p<n*m-1 and vis[g[p+1].x][g[p+1].y]==false){
p++;//注意下标的使用,谨防越界
vis[g[p].x][g[p].y]=true;
rep(x,g[p].x-1,1){
if(g[p].y==m or vis[x][g[p].y+1])break;注意下标的使用,谨防越界
per(y,g[p].y+1,m){
if(vis[x][y])break;
vis[x][y]=true;
}
}
per(x,g[p].x+1,n){
if(g[p].y==1 or vis[x][g[p].y-1])break;//注意下标的使用,谨防越界
rep(y,g[p].y-1,1){
if(vis[x][y])break;
vis[x][y]=true;
}
}
}
cout<<p+1<<endl;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}
循环里面的两个break是不能省的,总复杂度N*M,可以通过此题。