题意:大致意思是给一个n*m的01矩阵,起点为左上方(1,1),终点为右下方(n,m),求从左上方到右下方字典序自小的路径,如果路径都为0,则输出0。
分析:首先字典序最小,先要满足路径最短,再满足路径的值最小,路径最短的毫无疑问是越靠下或者越靠右,而且如果路径的前面为0,则可以认为是以第一个非0的点的为起点。因此这题可以转化为:先找出以起点为中心的连续为零的集合,再在其中找出x+y最大的一些点为起点,(当然如果s[1][1]==1,起点就是左上方),然后以这些点不断往下往右寻找下一排的最小值,然后以下一排的最小值的为起点的所有点再往下递推,直到终点。(这里每一排的递推的起点和终点推理可以画图推出),也就是用dfs搜四个方向,递推两个方向向下和向右。
图中的点值为x+y,当tot=2时,下一排的x的起点为tot-m>=tot-m?1为1,终点为i-n<=1?i-1:n,为1,后面的斜排同理
2 | 3 | 4 |
3 | 4 | 5 |
4 | 5 | 6 |
注意:之前博客提到的,由于这里用到dfs,而且栈最大要开4000000,(但是用bfs写就不需要了)所以在hdu交的话要手动扩栈,而且要用c++交。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include<vector>
#pragma comment(linker,"/STACK:1024000000,1024000000") //手动扩栈
using namespace std;
const int maxn = 1e3+5;
int n,m;
int tot;
char s[maxn][maxn];
bool vis[maxn][maxn];
void dfs(int x,int y)
{
if(vis[x][y]||s[x][y]-'0') return;//边界条件
vis[x][y]=1; //能走到的点标记
if(x+y>tot) tot=x+y; //标记最靠下的斜线的x+y
if(x<n) dfs(x+1,y); //向下
if(x>1) dfs(x-1,y); //向上
if(y<m) dfs(x,y+1); //向右
if(y>1) dfs(x,y-1); //向左
}
void solve()
{
if(s[1][1]=='1'){
printf("1");
vis[1][1]=1;
}
else if(s[1][1]=='0'&&tot==m+n){
printf("0");
}
for(int i=tot;i<n+m;i++){
int nw = 1;
for(int j=(i-m>=1?i-m:1);j<=(i-n<=1?i-1:n);j++){//斜线的初始点到终点
int nx = j;
int ny = i-j;
if(vis[nx][ny]){//由上一排得到能走的点
if(nx<n) nw = min(s[nx+1][ny]-'0',nw);//右边
if(ny<m) nw = min(s[nx][ny+1]-'0',nw);//下边
}
}
for(int j=(i-m>=1?i-m:1);j<=(i-n<=1?i-1:n);j++){
int nx = j;
int ny = i-j;
if(vis[nx][ny]){
if(nx < n && s[nx+1][ny]-'0'==nw) vis[nx+1][ny]=1;//标记下一排能走的点
if(ny < m && s[nx][ny+1]-'0'==nw) vis[nx][ny+1]=1;
}
}
printf("%d",nw);
}
printf("\n");
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis));
tot=2;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
dfs(1,1);
solve();
}
}