算法训练 DAY-2

一、马的遍历

题目链接

题目描述

有一个 n × m n \times m n×m 的棋盘,在某个点 ( x , y ) (x, y) (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。

输入格式

输入只有一行四个整数,分别为 n , m , x , y n, m, x, y n,m,x,y

输出格式

一个 n × m n \times m n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 − 1 -1 1)。

样例输入 #1

3 3 1 1

样例输出 #1

0    3    2    
3    -1   1    
2    1    4

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ x ≤ n ≤ 400 1 \leq x \leq n \leq 400 1xn400 1 ≤ y ≤ m ≤ 400 1 \leq y \leq m \leq 400 1ym400

代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
#define ios ios::sync_with_stdio(0); cin.tie(0),cout.tie(0);
const int N = 100010,M = 2010,INF = 0x3f3f3f3f;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int n,m,xx,yy;
int a[M][M];
int d[M][M];
bool st[M][M];
queue<PII> q;
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};
void bfs()
{
	memset(d,-1,sizeof d);
	memset(st,false,sizeof st);
	d[xx][yy] = 0;
	q.push({xx,yy});
	st[xx][yy] = true;
	while(!q.empty())
	{
		auto t = q.front();
		q.pop();
		int disx = t.first;
		int disy = t.second;
		for(int i = 0;i < 8;i ++)
		{
			int x = disx + dx[i];
			int y = disy + dy[i];
			if(x >= 1 && x <= n && y >= 1 && y <= m && !st[x][y])
			{
				d[x][y] = d[disx][disy] + 1;
				q.push({x,y});
				st[x][y] = true;
			}
		}
	}
}
void solve()
{
    cin>>n>>m>>xx>>yy;
    bfs();
    for(int i = 1;i <= n;i ++)
    {
    	for(int j = 1;j <= m;j ++)
    	{
    		cout<<d[i][j]<<' ';
    	}
    	cout<<endl;
    }
}
int main()
{
    ios
    int t = 1;
    while(t --)
    {
        solve();
        cout<<endl;
    }
    return 0;
}

总结

bfs模板题,很经典的走路问题,需要注意的是要记录走过的点是否走过,否则会重复计算陷入死循环。

二、血色先锋队

题目链接

题目背景

巫妖王的天灾军团终于卷土重来,血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团,以及一切沾有亡灵气息的生物。孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重包围,现在他们将主力只好聚集了起来,以抵抗天灾军团的围剿。可怕的是,他们之中有人感染上了亡灵瘟疫,如果不设法阻止瘟疫的扩散,很快就会遭到灭顶之灾。大领主阿比迪斯已经开始调查瘟疫的源头。原来是血色先锋军的内部出现了叛徒,这个叛徒已经投靠了天灾军团,想要将整个血色先锋军全部转化为天灾军团!无需惊讶,你就是那个叛徒。在你的行踪败露之前,要尽快完成巫妖王交给你的任务。

题目描述

军团是一个 n n n m m m 列的矩阵,每个单元是一个血色先锋军的成员。感染瘟疫的人,每过一个小时,就会向四周扩散瘟疫,直到所有人全部感染上瘟疫。你已经掌握了感染源的位置,任务是算出血色先锋军的领主们感染瘟疫的时间,并且将它报告给巫妖王,以便对血色先锋军进行一轮有针对性的围剿。

输入格式

1 1 1 行:四个整数 n n n m m m a a a b b b,表示军团矩阵有 n n n m m m 列。有 a a a 个感染源, b b b 为血色敢死队中领主的数量。

接下来 a a a 行:每行有两个整数 x x x y y y,表示感染源在第 x x x 行第 y y y 列。

接下来 b b b 行:每行有两个整数 x x x y y y,表示领主的位置在第 x x x 行第 y y y 列。

输出格式

1 1 1 b b b 行:每行一个整数,表示这个领主感染瘟疫的时间,输出顺序与输入顺序一致。如果某个人的位置在感染源,那么他感染瘟疫的时间为 0 0 0

样例 #1

样例输入 #1

5 4 2 3
1 1
5 4
3 3
5 3
2 4

样例输出 #1

3
1
3

提示

输入输出样例 1 解释

如下图,标记出了所有人感染瘟疫的时间以及感染源和领主的位置。

数据规模与约定

对于 100 % 100\% 100% 的数据,保证 1 ≤ n , m ≤ 500 1\le n,m\le500 1n,m500 1 ≤ a , b ≤ 1 0 5 1\le a,b\le10^5 1a,b105

代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
#define ios ios::sync_with_stdio(0); cin.tie(0),cout.tie(0);
const int N = 100010,M = 2010,INF = 0x3f3f3f3f;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
queue<PII> q;
int n,m,a,b;
int d[M][M];
bool st[M][M];
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
void bfs()
{
	while(!q.empty())
	{
		auto t = q.front();
		q.pop();
		int xd = t.first;
		int yd = t.second;
		for(int i = 0;i < 4;i ++)
		{
			int a = xd + dx[i];
			int b = yd + dy[i];
			if(a >= 1 && a <= n && b >= 1 && b <= m && !st[a][b] && d[a][b] == -1)
			{
				d[a][b] = d[xd][yd] + 1;
				st[a][b] = true;
				q.push({a,b});
			}
		}
	}
}
void solve()
{
	
	cin>>n>>m>>a>>b;
	memset(st,false,sizeof st);
	memset(d,-1,sizeof d);
	for(int i = 0;i < a;i ++)
	{
		int xx,yy;
		cin>>xx>>yy;
		q.push({xx,yy});
		d[xx][yy] = 0;
		st[xx][yy] = true;
	}
	bfs();
	for(int i = 0;i < b;i ++)
	{
		int xx,yy;
		cin>>xx>>yy;
		cout<<d[xx][yy]<<endl;
	}
	// for(int i = 1;i <= n;i ++)
	// {
	// 	for(int j = 1;j <= m;j ++)
	// 	{
	// 		cout<<d[i][j]<<' ';
	// 	}
	// 	cout<<endl;
	// }
}
int main()
{
    ios
    int t = 1;
    while(t --)
    {
        solve();
        cout<<endl;
    }
    return 0;
}

总结

开始把所有的感染者都添加到队列中,然后bfs,计算出感染到每个格子需要的时间存到数组d里,随后b次询问即可

三、01迷宫

题目链接

题目描述

有一个仅由数字 0 0 0 1 1 1 组成的 n × n n \times n n×n 格迷宫。若你位于一格 0 0 0 上,那么你可以移动到相邻 4 4 4 格中的某一格 1 1 1 上,同样若你位于一格 1 1 1 上,那么你可以移动到相邻 4 4 4 格中的某一格 0 0 0 上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入格式

第一行为两个正整数 n , m n,m n,m

下面 n n n 行,每行 n n n 个字符,字符只可能是 0 0 0 或者 1 1 1,字符之间没有空格。

接下来 m m m 行,每行两个用空格分隔的正整数 i , j i,j i,j,对应了迷宫中第 i i i 行第 j j j 列的一个格子,询问从这一格开始能移动到多少格。

输出格式

m m m 行,对于每个询问输出相应答案。

样例输入 #1

2 2
01
10
1 1
2 2

样例输出 #1

4
4

提示

所有格子互相可达。

  • 对于 20 % 20\% 20% 的数据, n ≤ 10 n \leq 10 n10
  • 对于 40 % 40\% 40% 的数据, n ≤ 50 n \leq 50 n50
  • 对于 50 % 50\% 50% 的数据, m ≤ 5 m \leq 5 m5
  • 对于 60 % 60\% 60% 的数据, n , m ≤ 100 n,m \leq 100 n,m100
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1000 1\le n \leq 1000 1n1000 1 ≤ m ≤ 100000 1\le m \leq 100000 1m100000

代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
#define ios ios::sync_with_stdio(0); cin.tie(0),cout.tie(0);
const int N = 100010,M = 10005,INF = 0x3f3f3f3f;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
queue<PII> q;
bool st[M][M];
char s[M][M];
int d[M][M];
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int n,m;
int bfs(int x , int y){
    int ans = 1;
    vector<PII> p;
    q.push({x,y});
    st[x][y]=1;
    p.push_back({0,0});
    p.push_back({x,y});
    while(!q.empty())
    {
        auto t = q.front();
        int a = t.first;
        int b = t.second;
        q.pop();
        for(int i=0;i<4;i++)
        {
            x=a+dx[i];
            y=b+dy[i];
            if(s[x][y]!= s[a][b] && s[x][y]!='\0' && !st[x][y])
            {
                q.push({x,y});
                ans++;
                st[x][y]=1;
                p.push_back({x,y});
            }
        }
    }
    for(int i=1;i<=ans;i++)
        d[p[i].first][p[i].second]= ans;
    return ans;
}
void solve()
{
	
	cin>>n>>m;
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1 ;j <= n;j ++)
		{
			cin>>s[i][j];
		}
	}
	for(int j = 1;j <= m;j ++)
	{
		int aa,bb;
		cin>>aa>>bb;
		if(st[aa][bb])
		{
			cout<<d[aa][bb];
		}else
		{
			cout<<bfs(aa,bb);
		}
		cout<<endl;
	}
}
int main()
{
    ios
    int t = 1;
    while(t --)
    {
        solve();
        cout<<endl;
    }
    return 0;
}

总结

这个题有个规律能发现,所有联通的块到达的步数是一样的,所以遇到一个没有走过的区域时,将与它所有联通的部分都初始化成一样的步数,之后再遇到联通的区域时直接输出答案就好了,这里要多开一个vector记录联通块里区域的坐标,方便之后统一赋值

四、Dungeon Master

题目链接

题目描述

You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides.

Is an escape possible? If yes, how long will it take?

输入描述

The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size).
L is the number of levels making up the dungeon.
R and C are the number of rows and columns making up the plan of each level.
Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a ‘#’ and empty cells are represented by a ‘.’. Your starting position is indicated by ‘S’ and the exit by the letter ‘E’. There’s a single blank line after each level. Input is terminated by three zeroes for L, R and C.

输出描述

Each maze generates one line of output. If it is possible to reach the exit, print a line of the form
Escaped in x minute(s).

where x is replaced by the shortest time it takes to escape.
If it is not possible to escape, print the line
Trapped!

输入样例1

3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###

0 0 0

输出样例1

Escaped in 11 minute(s).
Trapped!

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
#define ios ios::sync_with_stdio(0); cin.tie(0),cout.tie(0);
const int N = 100010,M = 100,INF = 0x3f3f3f3f;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int l,r,c;
struct node
{
  int x,y,z;
};
char g[M][M][M];
int dis[M][M][M];
int dx[6]={1,-1,0,0,0,0};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={0,0,0,0,1,-1};
int bfs(node a,node b)
{
    memset(dis,-1,sizeof dis);
    queue<node> q;
    dis[a.x][a.y][a.z] = 0;
    q.push(a);
    while(!q.empty())
    {
        node t = q.front();
        q.pop();
        for(int i = 0;i < 6;i ++)
        {
            int x = t.x + dx[i];
            int y = t.y + dy[i];
            int z = t.z + dz[i];
            if(x >= 0 && x < l && y >= 0 && y < r && z >= 0 && z < c
                && dis[x][y][z] == -1 && g[x][y][z] != '#')
            {
                dis[x][y][z] = dis[t.x][t.y][t.z] + 1;
                if(x == b.x && y == b.y && z == b.z)
                {
                    return dis[x][y][z];
                }
                q.push({x,y,z});
            }
        }
    }
    return -1;
}
void solve()
{

}
int main()
{
    while(1)
    {
        cin>>l>>r>>c;
        if(l+r+c == 0) return 0;
        node st,ed;
        for(int i = 0;i < l;i ++)
        {
            for (int j = 0; j < r; j++)
            {
                for (int k = 0; k < c; k++)
                {
                    cin>>g[i][j][k];
                    char c = g[i][j][k];
                    if(c == 'S')
                    {
                        st = {i,j,k};
                    }else if(c == 'E')
                    {
                        ed = {i,j,k};
                    }
                }
            }
        }
        int ans = bfs(st,ed);
        if(ans == -1) cout<<"Trapped!"<<endl;
        else cout<<"Escaped in "<<ans<<" minute(s)."<<endl;
    }
    return 0;
}

总结

三维搜索的板子题

五、Catch That Cow

题目链接

题目描述

Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.

  • Walking: FJ can move from any point X to the points X - 1 or X + 1 in a single minute
  • Teleporting: FJ can move from any point X to the point 2 × X in a single minute.

If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?

输入描述
Line 1: Two space-separated integers: N and K

输出描述
Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.

输入样例1

5 17

输出样例1

4

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
#define ios ios::sync_with_stdio(0); cin.tie(0),cout.tie(0);
const int N = 100010,M = 2010,INF = 0x3f3f3f3f;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int n;

void bfs(int u)
{
	queue<LL> q;
	q.push(1);
	while(!q.empty())
	{
		LL t = q.front();
		q.pop();
		if(t%u == 0)
		{
			cout<<t<<endl;
			return;
		}
		q.push(t*10);
		q.push(t*10+1);
	}
}
int main()
{
	ios;
    while(1)
    {
    	cin>>n;
    	if(!n) break;
    	bfs(n);
    }
    return 0;
}

总结

往队列里添加所有的情况,条件符合时输出就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

唯念月色凉_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值