P1535 [USACO08MAR] Cow Travelling S

6 篇文章 0 订阅
4 篇文章 0 订阅

[USACO08MAR] Cow Travelling S

题目描述

奶牛们在被划分成 N N N M M M 列( 2 ≤ N , M ≤ 100 2 \leq N,M \leq 100 2N,M100)的草地上游走, 试图找到整块草地中最美味的牧草。

Farmer John 在某个时刻看见贝茜在位置 ( R 1 , C 1 ) (R_1, C_1) (R1,C1),恰好 T T T 0 < T ≤ 15 0 \lt T \leq 15 0<T15)秒后,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];	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值