http://acm.hdu.edu.cn/showproblem.php?pid=3912
这题应该算是个模拟题。题意大概是这样的:
一个n*m的网格,在第一行有个入口,第n行有个出口。然后人在里面行走必须满足如下条件:如果能向右走,就向右走,若不能向右走就向前走,如果不能向前走就向左走,如果不能向左走就像后走。题目中明确规定了可以保证从入口走到出口。问你的是:先从入口走到出口,再从出口走到入口,问能否遍历所有的网格。
因为题目所给的网格是有墙的。开始傻傻的我竟然用两个三维数组来存墙的位置,结果TLE,后来去食堂吃饭想起来可以用结构体存啊(too_young_too_simple),于是回来敲,结果TLE。次奥,当时就无语了。后来还是在巍神的启发下用了vis[4][N][N],这个数组的作用是存该点的可以走的方向 ===
我感受到了智商的压制 (大哭。。/(ㄒoㄒ)/~~
于是敲了代码,结果WA了。原因是有个地方没有注意到,我dfs的终止条件是当 (x==n&&y==ed)就退出,忽略了这种情况:
我的代码是只要到达这个网格就退出(红线),事实证明它还要往前走一步(黑线)。所以我在当先的地图上加了两个多余的网格。
反思: 谢谢巍神的 vis[4][N][N] 数组,以后长点心眼。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mset(x,y) memset(x,y,sizeof(x));
using namespace std;
const int N = 500+10;
int n,m,st,ed;
bool Map[N][N];
int cnt;
bool vis[4][N][N];//奇妙的数组
int dir[4][2]={1,0,0,-1,-1,0,0,1};
bool check(int x,int y,int xx,int yy,int i)
{
if(xx==0&&yy==st+1) return true;
if(xx==n+1&&yy==ed+1) return true;
if(xx<1||xx>n) return false;
if(yy<1||yy>m) return false;
if(!vis[i][x][y]) return false;//一句话判掉不符合情况的
return true;
}
void work(int x,int y,int aim,int op)
{
if(op==1&&x==n+1&&y==ed+1) return;//注意是 (n+1行
if(op==2&&x==0&&y==st+1) return;//递归终止条件变了
int tmp,xx,yy;
for(int i=0;i<4;i++)
{ //本题的模拟关键处
if(i==0) tmp=(aim+1)%4;
else if(i==1) tmp=aim;
else if(i==2) tmp=((aim-1)%4+4)%4;
else tmp=(aim+2)%4;
xx=x+dir[tmp][0];
yy=y+dir[tmp][1];
if(!check(x,y,xx,yy,tmp)) continue;
if(Map[xx][yy]) cnt--,Map[xx][yy]=false;
break;
}
work(xx,yy,tmp,op);
return;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
mset(vis,true);
mset(Map,true);
scanf("%d%d%d%d",&n,&m,&st,&ed);
for(int i=1;i<=2*n-1;i++)
{
int x;
if(i%2)
{
for(int j=1;j<m;j++)
{
scanf("%d",&x);
if(x==1) vis[3][(i+1)/2][j]=vis[1][(i+1)/2][j+1]=false;//!!!
}
}
else
{
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
if(x==1) vis[0][i/2][j]=vis[2][i/2+1][j]=false;//!!!
}
}
}
cnt=n*m;
Map[1][st+1]=Map[0][st+1]=Map[n+1][ed+1]=false;
cnt--;
work(1,st+1,0,1);
work(n,ed+1,2,2);
puts(cnt==0?"YES":"NO");
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=1254
题意不用说,以前玩过的小游戏。
需要注意的地方:
- 人可以走箱子的终点,也就是 3 这个位置
- 判断箱子是否可以推的前提是 人是否能到达箱子的另外一个方向
- 箱子要推的地方不能是墙,而且箱子可以经过一个地方两次
- 标记的时候用vis[4][N][N]标记人的位置和推箱子的方向(因为箱子可以到达一个地方两次,显然标记箱子没用;标记人的地方和在这里人往哪个方向推箱子)
- 推箱子的时候箱子前进一步但是人不会动
- 还有就是要细心耐心的debug…
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 10;
int Map[N][N];
bool vis[4][N][N];
struct Node
{
int posx,posy;//当前箱子的位置
int xx,yy;//当前人的位置
int cnt;
};
int n,m;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
struct Node1
{
int x;int y;
};
bool check(int x,int y)
{
if(x<1||x>n) return false;
if(y<1||y>m) return false;
if(Map[x][y]==1) return false;
return true;
}
bool judge(int x,int y,int aimx,int aimy,int posx,int posy)//判断人是否可以到达箱子的另一端
{
bool vvis[N][N];
memset(vvis,true,sizeof(vvis));
Node1 now,next;
queue<Node1> que;
now.x=x;now.y=y;
vvis[x][y]=false;
que.push(now);
while(!que.empty())
{
now=que.front(); que.pop();
if(now.x==aimx &&now.y==aimy) return true;
for(int i=0;i<4;i++)
{
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(!check(next.x,next.y)) continue;
if(!vvis[next.x][next.y]) continue;
if(next.x==posx&&next.y==posy) continue;
vvis[next.x][next.y]=false;
que.push(next);
}
}
return false;
}
int bfs(int x1,int y1,int x2,int y2)
{
Node now,next;
queue<Node> que;
now.posx=x1;now.posy=y1;
now.xx=x2; now.yy=y2;
now.cnt=0;
que.push(now);
while(!que.empty())
{
now=que.front();
que.pop();
if(Map[now.posx][now.posy]==3) return now.cnt;
for(int i=0;i<4;i++)
{
int aimx=now.posx-dir[i][0];
int aimy=now.posy-dir[i][1];
if(!judge(now.xx,now.yy,aimx,aimy,now.posx,now.posy)) continue;
if(!vis[i][aimx][aimy]) continue;
next.posx=now.posx+dir[i][0];
next.posy=now.posy+dir[i][1];
if(!check(next.posx,next.posy)) continue;
next.xx=aimx; next.yy=aimy;//更新人的位置
vis[i][aimx][aimy]=false;
next.cnt=now.cnt+1;
que.push(next);
}
}
return -1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int posx=1,posy=1,xx=1,yy=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&Map[i][j]);
if(Map[i][j]==2) posx=i,posy=j;
if(Map[i][j]==4) xx=i,yy=j;
}
memset(vis,true,sizeof(vis));
printf("%d\n",bfs(posx,posy,xx,yy));
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=1983
题意很简单,全是中文。
这题让我惊讶的地方就是 暴力啊暴力啊暴力啊,从未见过如此暴力的题目。
因为要封锁的地方绝对是<=4的,所以暴力从小到大枚举可能封锁的区域,直到满足情况为止。
暴力出奇迹:既然时间给的是5秒就不要怂,暴力搞起来,被吓到了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N =10;
char Map[N][N];
int n,m,sx,sy,k;
struct Node
{
int x,y,cnt;
int has;
};
int dir[4][2]={0,1,1,0,0,-1,-1,0};
bool bfs()
{
bool vis[10][10][2];
memset(vis,true,sizeof(vis));
Node now,next;
queue<Node> que;
now.x=sx; now.y=sy;
now.cnt=0;
now.has=0;
vis[now.x][now.y][0]=false;
que.push(now);
while(!que.empty())
{
now=que.front();
que.pop();
if( Map[now.x][now.y]=='E' && now.has &&now.cnt<=k ) return false;
if(now.cnt>k) continue;
for(int i=0;i<4;i++)
{
next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(next.x<1||next.x>n||next.y<1||next.y>m) continue;
if(!vis[next.x][next.y][now.has]||Map[next.x][next.y]=='#') continue;
next.has=now.has;
if(Map[next.x][next.y]=='J') next.has=1;
vis[next.x][next.y][next.has]=false;
next.cnt=now.cnt+1;
que.push(next);
}
}
return true;
}
bool dfs(int t)
{
if(!t) return bfs();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(Map[i][j]=='#'||Map[i][j]=='E') continue;
char op=Map[i][j];
Map[i][j]='#';
if(dfs(t-1)) return true;
Map[i][j]=op;
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf(" %c",&Map[i][j]);
if(Map[i][j]=='S') sx=i,sy=j;
}
bool flag=false;
for(int i=0;i<4;i++)
{
if(dfs(i)) { flag=true;printf("%d\n",i);break;}
}
if(!flag) printf("4\n");
}
return 0;
}
That’s all :)