[USACO08MAR] Cow Travelling S
题目描述
奶牛们在被划分成 N N N 行 M M M 列( 2 ≤ N , M ≤ 100 2 \leq N,M \leq 100 2≤N,M≤100)的草地上游走, 试图找到整块草地中最美味的牧草。
Farmer John 在某个时刻看见贝茜在位置 ( R 1 , C 1 ) (R_1, C_1) (R1,C1),恰好 T T T( 0 < T ≤ 15 0 \lt T \leq 15 0<T≤15)秒后,FJ 又在位置 ( R 2 , C 2 ) (R_2, C_2) (R2,C2) 与贝茜撞了正着。FJ 并不知道在这 T T T 秒内贝茜是否曾经到过 ( R 2 , C 2 ) (R_2, C_2) (R2,C2),他能确定的只是,现在贝茜在那里。
设 S S S 为奶牛在 T T T 秒内从 ( R 1 , C 1 ) (R_1, C_1) (R1,C1) 走到 ( R 2 , C 2 ) (R_2, C_2) (R2,C2) 所能选择的路径总数,FJ 希望有 一个程序来帮他计算这个值。每一秒内,奶牛会水平或垂直地移动 1 1 1 单位距离(奶牛总是在移动,不会在某秒内停在它上一秒所在的点)。草地上的某些地方有树,自然,奶牛不能走到树所在的位置,也不会走出草地。
现在你拿到了一张整块草地的地形图,其中 .
表示平坦的草地,*
表示挡路的树。你的任务是计算出,一头在
T
T
T 秒内从
(
R
1
,
C
1
)
(R_1, C_1)
(R1,C1) 移动到
(
R
2
,
C
2
)
(R_2, C_2)
(R2,C2) 的奶牛可能经过的路径有哪些。
输入格式
第一行包含 3 3 3 个用空格隔开的整数: N , M , T N,M,T N,M,T。
接下来
n
n
n 行:第
i
i
i 行为
M
M
M 个连续的字符,描述了草地第
i
i
i 行各点的情况,保证字符是 .
和 *
中的一个。
最后一行 4 4 4 个整数 R 1 , C 1 , R 2 , C 2 R_1,C_1,R_2,C_2 R1,C1,R2,C2。
输出格式
输出从 ( R 1 , C 1 ) (R_1, C_1) (R1,C1) 移动到 ( R 2 , C 2 ) (R_2, C_2) (R2,C2) 的方案数。
样例 #1
样例输入 #1
4 5 6
...*.
...*.
.....
.....
1 3 1 5
样例输出 #1
1
提示
奶牛在 6 6 6 秒内从 ( 1 , 3 ) (1,3) (1,3) 走到 ( 1 , 5 ) (1,5) (1,5) 的方法只有一种,绕过她面前的树。
思路1(bfs)
首先要理解好题意,题目说的是恰好
T
T
T秒后,而且也可以重复经过,只要在最后那一秒到达了指定点就行,开始我理解题目错了,我以为T秒内就行了。
我开始是用bfs做的,然后超时和超内存了,因为没有剪枝,入队的太多了,然后我参考了别人的。因为某个点可以反复经过,能唯一确定此状态的就是位置+时间,我们可以用一个三维数组vis[i][j][k]来记录此时此位置的方案数,(i,j)代表位置,k代表某一时刻。vis[i][j][k]>0则就代表此时此位置已经被经过了,经过了就跳过,否则入队,这就实现了剪枝。
下面来看代码吧!
代码
#include<bits/stdc++.h>
using namespace std;
char a[105][105];
int n,m,t,sx,sy,ex,ey;
int dx[4]={0,0,1,-1}; //方向
int dy[4]={1,-1,0,0};
int vis[105][105][20];
struct node{
int x;
int y;
int ti; //时间
};
void bfs()
{
queue<node>q;
q.push({sx,sy,0});
vis[sx][sy][0]=1; //刚开始的时候初始化为1,只有1个方案数
while(q.size())
{
node u=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int tx=u.x+dx[i],ty=u.y+dy[i];
if(vis[tx][ty][u.ti+1]) //经过了此状态
{
vis[tx][ty][u.ti+1]+=vis[u.x][u.y][u.ti]; //记得加上前一个点的方案数,虽然不入队,但是还是要更新方案数的
continue;
}
if(tx>n||ty>m||tx<1||ty<1||a[tx][ty]=='*'||u.ti+1>t)//超出范围或碰到树或时间超出了则跳过
continue;
vis[tx][ty][u.ti+1]+=vis[u.x][u.y][u.ti]; //这里也是要更新方案数的
q.push({tx,ty,u.ti+1}); //入队
}
}
}
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
cin>>sx>>sy>>ex>>ey;
bfs();
cout<<vis[ex][ey][t]; //输出最后的状态
}
思路2(dfs)
用dfs,也是要剪枝的,不然就超时了,那么怎么剪枝呢?
1.time到规定时间了,返回
2.abs(终点的x-此时的x)+abs(终点的y-此时的y)>t-time(剩余时间),返回
具体来看代码吧
代码
#include<bits/stdc++.h>
using namespace std;
int ans;
char a[105][105];
int n,m,t,sx,sy,ex,ey;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
struct node{
int x;
int y;
int ti;
};
void dfs(int tx,int ty,int time)
{
if(tx==ex&&ty==ey&&time==t) ans++; //记录方案数
if(time==t||abs(tx-ex)+abs(ty-ey)>t-time) return; //剪枝
for(int i=0;i<4;i++)
{
int nx=tx+dx[i],ny=ty+dy[i];
if(nx<1||ny<1||nx>n||ny>m||a[nx][ny]=='*') continue; //超出范围或碰到树则跳过
dfs(nx,ny,time+1);
}
}
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
cin>>sx>>sy>>ex>>ey;
dfs(sx,sy,0);
cout<<ans;
}
思路3(动态规划)
这个思路也是学习别人的,觉得自己的能力还是想不出来的
1.在1秒内,对于第i行第j列的到达方案数为上下左右的和。
我们这里可以用−1表示树。
2.但是,如果周围有树,只能加没有树的一边。
3.注意,整个过程用两个数组操作,b数组为这一秒后的方案数,注意,每次计算都要先清0。最后在赋值到a数组。
#include<bits/stdc++.h>
using namespace std;
int a[105][105],b[105][105];
int n,m,t,sx,sy,ex,ey;
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char c;
cin>>c;
if(c=='*') a[i][j]=b[i][j]=-1;//树
}
cin>>sx>>sy>>ex>>ey;
a[sx][sy]=b[sx][sy]=1; //初始化
for(int k=1;k<=t;k++){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]!=-1)
{
b[i][j]=0;
if(a[i-1][j]!=-1) b[i][j]+=a[i-1][j];
if(a[i+1][j]!=-1) b[i][j]+=a[i+1][j];
if(a[i][j-1]!=-1) b[i][j]+=a[i][j-1];
if(a[i][j+1]!=-1) b[i][j]+=a[i][j+1];
} //判断是否是树,是树的话不能加
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=b[i][j];
}
cout<<a[ex][ey];
}