时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
晚上,跑男们来了节目的最后一站:江苏省扬州中学,完成最后一项比赛:撕名牌。撕名牌的地点是一个由n*n房间组成的正方形,每个房间里都有一个数字,表示从这个房间可以通过地道向右或向下穿过几个房间。从左上角开始,如果谁能安全到达右下角就算胜利。
这里4*4的方格中每一格表示进入这个房间时,队员可以向右或向下穿过的房间数。
郑恺是奔跑小王子,当他拿到这张地图时,脸都变绿了,速度再快,进了迷宫一样的房间也是没办法啊,还好参加JSOI2015夏令营的小伙伴都在,你能帮帮他算出从左上角可以到达右下角的路径数目吗?
输入
第一行为一个整数n,表示棋盘的大小。
以下有n行,每行有n个数字(数字与数字之间有一个空格隔开),表示在相应的格子内,棋子可以向右或向下跳跃的格子数。
输出
输出共一行,包含一个数,表示从左上角可以到达右下角的路径数目。
样例输入 Copy
4
2 3 3 1
1 2 1 3
1 2 3 1
3 1 1 0
样例输出 Copy
3
提示
对于100%的数据,1≤n≤100。
一开始看到数据量最大为100,感觉dfs可能会超时,就尝试bfs+queue,结果内存超限(???),后来尝试在bfs的基础上判断该点是否被访问过,这下子答案又不对了。。。
其实模拟一下样例,就会发现类似于“先访问的结点,它的孩子先访问”这种特点,这也是为什么我先想到用bfs+queue的原因。后来静下心来想想,用dfs也可以,不过一定一定要剪枝,否则还会超时。这里剪枝的条件是移动后的横坐标或纵坐标超出范围,不难想到运行时可能会有相当一部分解会被剪枝。统计条数的条件时搜索到(n,n),同时结束递归。然后就是dfs的模板了,vis数组用于标记某点是否被访问过。
#include<cstdio>
using namespace std;
int pane[105][105];
int vis[105][105]={0};
int n,cnt=0;
void f(int x,int y)
{
if(x>=n+1||y>=n+1)return;
if(x==n&&y==n)
{
cnt++;
return;
}
if(x+pane[x][y]<=n&&vis[x+pane[x][y]][y]==0)//向右移动
{
vis[x+pane[x][y]][y]=1;
f(x+pane[x][y],y);//搜索右侧的点
vis[x+pane[x][y]][y]=0;
}
if(y+pane[x][y]<=n&&vis[x][y+pane[x][y]]==0)//向下移动
{
vis[x][y+pane[x][y]]=1;
f(x,y+pane[x][y]);//搜索下侧的点
vis[x][y+pane[x][y]]=0;
}
}
int main()
{
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
scanf("%d",&pane[i][j]);
}
}
f(1,1);
printf("%d",cnt);
return 0;
}
/**************************************************************
Language: C++
Result: 正确
Time:43 ms
Memory:1204 kb
****************************************************************/