上海市计算机学会竞赛平台2023年8月月赛丙组 T5 方格路径

T5 方格路径

内存限制: 256 Mb时间限制: 1000 ms
题目描述
给定 n × m n×m n×m 个方格构成的图,每个格子都有一种地形:
有一些格子是墙,以符号 # 表示,墙不可通行。
有一些格子是空地,以符号 . 表示,空地可以通行。
请统计从左上角的方格出发,有多少种不同的路线可以以最短距离走到右下角。在行走过程中,不能进入地形为墙的方格,保证起点与终点方格地形不是墙。且行走时,只能移动到水平或垂直方向相邻的方格。
由于方案数可能很大,输出模 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的余数。
输入格式
第一行:单个整数 n n n m m m
第二行到第 n + 1 n+1 n+1 行:第 i + 1 i+1 i+1 行每行有 m m m 个整数表示第 i i i 行的地形。
输出格式
单个整数:表示路线方案模 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的余数。
数据范围
30% 的数据, 1 ≤ n , m ≤ 4 1≤n,m≤4 1n,m4
60% 的数据, 1 ≤ n , m ≤ 10 1≤n,m≤10 1n,m10
100% 的数据, 1 ≤ n , m ≤ 1000 1≤n,m≤1000 1n,m1000
样例数据
输入:
3 3

.#.

输出:
2

题目给定了 n × m n×m n×m 的图形,‘#’是障碍,‘.’是通路。
而与普通的最优路径问题不同的是此题问的是最优路径的条数。

方法一:宽搜找最短路径步数+深搜搜索路径条数(70分)

深搜中的剪枝与最优化剪枝不同,当步数相等时最优化剪枝会剪掉,因为要寻找的是路径长度,但是现在找的是路径条数,所以不能剪。
另外,结构体中的 p o s pos pos 表示步数, x x x y y y 表示坐标。

#include <bits/stdc++.h>
#define int long long
#define inf 1000000007
using namespace std;

int n,m,dp[1010][1010],dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},num,cnt;
char a[1010][1010];

struct nod{
	int x,y,pos;	
}q[1000010];

int bfs(){
	int head=1,tail=2;
	q[1].x=1,q[1].y=1,q[1].pos=0;
	while(head<tail){
		if(q[head].x==n&&q[head].y==m)return q[head].pos;
		int x=q[head].x,y=q[head].y;
		for(int i=0;i<4;++i){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx>0&&xx<=n&&yy>0&&yy<=m&&dp[xx][yy]==-1&&a[xx][yy]=='.'){
				q[tail].x=xx,q[tail].y=yy,q[tail].pos=q[head].pos+1;
          		dp[xx][yy]=1;
				++tail;
			}
		}
		++head;
	}
}

void f(int x,int y,int s){
	if(s>=num){
      if(x==n&&y==m&&s==num)cnt=(cnt+1)%inf;
      return;
    }
	dp[x][y]=s;
	for(int i=0;i<4;++i){
		int xx=x+dx[i],yy=y+dy[i];
		if((xx>0&&xx<=n&&yy>0&&yy<=m)&&(dp[xx][yy]==-1||dp[xx][yy]>=s+1)&&a[xx][yy]=='.')
			f(xx,yy,s+1);
	}
}

signed main(){
	memset(dp,-1,sizeof(dp));
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			cin>>a[i][j];
	num=bfs();
	memset(dp,-1,sizeof(dp));
	f(1,1,0);
	cout<<cnt<<endl;
	return 0;
}

此时TLE三个点。

方法2:宽搜路径(STL栈)(70分)

如果简单粗暴开个10,000,000数组肯定因数组不够越界而RE,所以在用了STL栈之后变为TLE。
其本质仍然是枚举每一条路径并计数,题目却要求模一个高达10亿的数,由此可见最坏计数次数必定大于10亿,超时不可避免。注意剪枝与上文同。
而现在的宽搜程序正是答案的基础。

#include <bits/stdc++.h>
#define int long long
#define inf 1000000007
using namespace std;

int n,m,dp[1010][1010],dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},num=LONG_LONG_MAX,ans;
char a[1010][1010];

struct nod{
	int x,y,pos;	
};

queue<nod>q;

void bfs(){
	q.push({1,1,0});
	while(!q.empty()){
		if(q.front().x==n&&q.front().y==m&&q.front().pos<=num){
			num=q.front().pos;
			ans=(ans+1)%inf;
		}
		int x=q.front().x,y=q.front().y;
		if(q.front().pos<num)
			for(int i=0;i<4;++i){
				int xx=x+dx[i],yy=y+dy[i];
				if(xx>0&&xx<=n&&yy>0&&yy<=m&&(dp[xx][yy]==-1||dp[xx][yy]==q.front().pos+1)&&a[xx][yy]=='.'){
					q.push({xx,yy,q.front().pos+1});
					dp[xx][yy]=q.back().pos;
				}
			}
		q.pop();
	}
}

signed main(){
	memset(dp,-1,sizeof(dp));
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			cin>>a[i][j];
	bfs();
	cout<<ans<<endl;
	return 0;
}

注意:Dev-c++ 5.5.2 可能会报警(入队写法),但是网站交是可以过的。不放心的同学可以手写自构函数。

方法3:宽搜改良(AC)

首先需要明白的是,因为宽搜的基本思想,所以步数消耗较大的状态一定在消耗少的后面,所以当枚举到第二个状态重复的点时前一个肯定还未出队,所以就可以把它们合并为一个状态。
定义结构体时新增 s t e p step step 变量用于存储当前状态的方案数,遇到状态重复时就可以将两个 s t e p step step 加到一起,合并在一个空间内。此时剪枝就可以改为普通宽度优先搜索的剪枝(因为每一个点分为三种情况:
1.第一次到达,单独占用空间。
2.步数消耗一样小合并状态。
3.步数消耗大,剪掉。)

#include <bits/stdc++.h>
#define int long long
#define inf 1000000007
using namespace std;

int n,m,dp[1010][1010],dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},num=LONG_LONG_MAX,ans,vh[1010][1010];
char a[1010][1010];

struct nod{
	int x,y,pos,step;	
}q[1000010];

void bfs(){
	int head=1,tail=2;
	q[1].x=1,q[1].y=1,q[1].pos=0,q[1].step=1;
	while(head<tail){
		if(q[head].x==n&&q[head].y==m&&q[head].pos<=num){
			num=q[head].pos;
			ans=(ans+q[head].step)%inf;
		}
		int x=q[head].x,y=q[head].y;
		if(q[head].pos<num)
			for(int i=0;i<4;++i){
				int xx=x+dx[i],yy=y+dy[i];
				if(xx>0&&xx<=n&&yy>0&&yy<=m&&(dp[xx][yy]==-1||dp[xx][yy]==q[head].pos+1)&&a[xx][yy]=='.'){
					if(dp[xx][yy]==q[head].pos+1)q[vh[xx][yy]].step=(q[vh[xx][yy]].step+q[head].step)%inf;
					else{
						vh[xx][yy]=tail;
						q[tail].x=xx,q[tail].y=yy,q[tail].pos=q[head].pos+1,q[tail].step=q[head].step;
						dp[xx][yy]=q[tail].pos;
						++tail;
					}
				}
			}
		++head;
	}
}

signed main(){
	memset(dp,-1,sizeof(dp));
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			cin>>a[i][j];
	bfs();
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
九宫格键盘是一种在手机键盘上常见的布局方式,主要用于输入中文和其他一些特殊符号。九宫格键盘将26个字母和其他输入选项分布在一个3x3的方格中,每个方格有一个或多个字母。用户通过在方格内滑动手指来选择字母进行输入。上海计算机学会在202010份举办了一场名为“c”的赛,其中丙组的题目包括了九宫格键盘的应用。 九宫格键盘在手机输入中具有一定的优势。首先,九宫格键盘相对于传统的QWERTY键盘,可以减少单个按键的尺寸,提高了输入的精度。其次,九宫格键盘将字母进行分组排列,大大降低了输入频率较高的字母之间的距离,提高了输入速度。此外,九宫格键盘还提供了一些特殊符号、数字和常用词语的快捷输入功能,使得输入更加方便和快捷。 针对上海计算机学会202010赛c丙组t3九宫格键盘c这个题目,应该是要求参赛选手设计一个基于九宫格键盘的输入法或者其他相关的应用程序。具体的题目要求可能涉及到输入法的设计原理、算法优化、界面设计等方面。参赛选手需要运用自己在计算机学会学习和实践过程中所掌握的知识,结合九宫格键盘的特点和应用场景,设计出一个创新性、实用性强的解决方案。 九宫格键盘在手机输入领域具有广泛的应用前景。随着智能手机的普及和人们对于更高效便捷的输入方式的需求,九宫格键盘的市场需求也在不断增加。因此,对于计算机学会的学生来说,参与这样的竞赛活动,不仅可以提高自己的技术能力和解决问题的能力,同时也能够加深对于九宫格键盘以及相关领域的理解和应用。这对于学生们今后的学习和发展都将起到积极的促进作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaoGuohao2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值