2020.7.31 模拟赛 题解

T1 营救

题目描述

铁塔尼号遇险了!他发出了求救信号。距离最近的哥伦比亚号收到了讯息,时间就是生命,必须尽快 赶到那里。

通过侦测,哥伦比亚号获取了一张海洋图。这张图将海洋部分分化成 n*n 个比较小的单位,其中用 1 标明的是陆地,用 0 标明是海洋。船只能从一个格子,移到相邻的四个格子。

为了尽快赶到出事地点,哥伦比亚号最少需要走多远的距离。

  • 输入格式:
    第一行为 n,下面是一个 n*n 的 0、1 矩阵,表示海洋地图。

    最后一行为四个小于 n 的整数,分别表示哥伦比亚号和铁塔尼号的位置。

  • 输出格式:
    哥伦比亚号到铁塔尼号的最短距离,答案精确到整数。

样例输入
3
001
101
100
1 1 3 3
样例输出
4
数据范围与提示

N<=1000

AC代码
// BFS板题 
#include <cstdio>
#include <queue>
using namespace std;

const int MAXN = 1005;
int mp[MAXN][MAXN]; // 存图 
int n; // 图的大小 
struct node {
	int x, y; // 坐标 
};
bool vis[MAXN][MAXN]; // 判断当前位置是否走过 
int dx[5] = {0, 1, 0, -1};
int dy[5] = {1, 0, -1, 0}; // 四个方向 
node st, ed; // 起始点和终点 
int ans[MAXN][MAXN]; // ans[i][j]表示到(i,j)需要的最短路程 

void bfs() { // 宽搜 
	queue<node> q;
	q.push(st);  
	vis[st.x][st.y] = true; 
	while(!q.empty()) {  
		node cur = q.front();
		q.pop();
		for(int i = 0; i < 4; i++) {  
			int cx = cur.x + dx[i];
			int cy = cur.y + dy[i];
			if(cx < 1 || cx > n) continue;
			if(cy < 1 || cy > n) continue;
			// 如果下一个点不合法,找下一个方向 
			if(vis[cx][cy] == false && mp[cx][cy] == 0) {
				// 如果没走过,且为海洋 
				vis[cx][cy] = true; // 标记下一个点为走过 
				node nxt;
				nxt.x = cx;
				nxt.y = cy;
				ans[nxt.x][nxt.y] = ans[cur.x][cur.y] + 1;  // 更新答案 
				if(nxt.x == ed.x && nxt.y == ed.y) { 
				// 如果下一个点就是终点?直接输出 
					printf("%d", ans[ed.x][ed.y]);
					return ;
				} 
				q.push(nxt); // 入队 
			}
		}
	}
	return ;
}

int main() {
	scanf ("%d", &n);
	for(int i = 1; i <= n; i++) {
		char s[MAXN];
		scanf ("%s", s + 1);
		for(int j = 1; j <= n; j++) 
			mp[i][j] = s[j] - '0';
	}
//	for(int i = 1; i <= n; i++) {
//		for(int j = 1; j <= n; j++) 
//			printf("%d ", mp[i][j]);
//		puts(" ");
//	}
	scanf ("%d %d %d %d", &st.x, &st.y, &ed.x, &ed.y);
	bfs();
	return 0; 
}

T2 关系网络

题目描述

有 n 个人,他们的编号为 1~n,其中有一些人相互认识,现在 x 想要认识 y,可以通过他所认识的人来认识更多的人(如果 a 认识 b,b 认识 c,那么 a 可以通过 b 来认识 c),求出 x 最少需要通过多少人才能认识 y。

  • 输入格式
    第 1 行 3 个整数 n、x、y,2≤n≤100; 接下来的 n 行是一个 n×n 的邻接矩阵, a[i][j]=1 表示 i 认识 j,a[i][j]=0 表示不认识。 保证 i=j 时,a[i][j]=0,并且 a[i][j]=a[j][i]。

  • 输出格式
    一行一个整数,表示 x 认识 y 最少需要通过的人数。数据保证 x 一定能认识 y。

输入样例

5 1 5
0 1 0 0 0
1 0 1 1 0
0 1 0 1 0
0 1 1 0 1
0 0 0 1 0

输出样例

2

AC代码
// 可以看作是求一个最短路
// 他需要通过多少人才能认识某个其他人
// 其实就是他最少需要经过多少个点才能到达终点 
#include <cstdio>
#include <cstring>
using namespace std;

const int MAXN = 105;
int cost[MAXN][MAXN];
// 利用邻接矩阵存图 
int dist[MAXN]; // 最短路 
bool vis[MAXN]; // 红蓝点集 
int n, x, y;

void dijkstra() { // 最短路dijkstra算法 
	memset(dist, 0x3F, sizeof dist);
	// 初值为最大,因为我们要找最小值 
	dist[x] = 0;
	for(int i = 1; i <= n; i++) {
		int mi = 0x3F3F3F3F, k = 0;
		for(int j = 1; j <= n; j++) { // 找到最小值 
			if(!vis[j] && dist[j] < mi) {
				mi = dist[j];
				k = j;
			}
		}
		vis[k] = true;
		// 加入红点集 
		for(int j = 1; j <= n; j++)
			if(cost[k][j] != 0 && dist[j] > dist[k] + 1)
			// 如果有边,且可以进行松弛,就直接松弛即可 
				dist[j] = dist[k] + 1;
	}
	return ;
}

int main() {
	scanf ("%d %d %d", &n, &x, &y);
	for(int i = 1; i <= n; i++) 
		for(int j = 1; j <= n; j++) {
			int w;
			scanf ("%d", &w);
			cost[i][j] = w;
			cost[j][i] = w;
			// 双向存边 
		}
	dijkstra();
	if(x != y) printf("%d ", dist[y] - 1); 
	// 减去第一条边,就是经过的点数 
	else printf("0");
	return 0;
}

T3 寻找道路

题目描述

在有向图G中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

路径上的所有点的出边所指向的点都直接或间接与终点连通。
在满足条件1的情况下使路径最短。
注意:图G中可能存在重边和自环,题目保证终点没有出边。

请你输出符合条件的路径的长度。

  • 输入格式
    第一行有两个用一个空格隔开的整数 n 和 m,表示图有 n 个点和 m 条边。

接下来的 m 行每行 2 个整数 x, y,之间用一个空格隔开,表示有一条边从点 x 指向点 y。

最后一行有两个用一个空格隔开的整数 s, t,表示起点为 s,终点为t 。

  • 输出格式
    输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出 -1。
样例输入 1
3 2
1 2
2 1
1 3
样例输出 1
-1
样例输入 1
6 6
1 2
1 3
2 6
2 5
4 5
3 4
1 5
样例输出 2
3
AC代码
// 在跑最短路之前,标记一下哪些点可以走,哪些不可以走
// 也就是以终点为起点,跑一遍dfs,在枚举每个点
// 看它们联通的点是否都和终点联通 
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

const int MAXN = 10005;
vector<int> s[MAXN];
vector<int> t[MAXN]; // 反向存边 
bool vis[MAXN]; // dijkstra 红蓝点集 
bool flag[MAXN]; // 是否与终点联通 
bool p[MAXN]; // 是否能走 
int dist[MAXN];
int n, m, S, T; // 点数n,边数m,起点S,终点T 

void dfs(int i) { // 从终点开始搜索 
	flag[i] = true;  
	for(int j = 0; j < t[i].size(); j++) // 枚举当前点连接的边 
		if(flag[t[i][j]] == false) // 如果没走过 
			dfs(t[i][j]); // 走过去 
	return ;
}

void dijkstra() { // 最短路dijkstra算法 
	memset(dist, 0x3f, sizeof dist);
	// 初值为最大,因为我们要找最小值
	dist[S] = 0;
	for(int i = 1; i <= n; i++) {
		int mi = 0x3f3f3f3f, k = 0;
		for(int j = 1; j <= n; j++) {
			if(!vis[j] && dist[j] < mi) { // 找到最小值 
				mi = dist[j];
				k = j;
			}
		}
		// 加入红点集 
		vis[k] = true;
		for(int j = 0; j < s[k].size(); j++) {
			int v = s[k][j];
			if(dist[v] > dist[k] + 1 && p[v] == true)
			// 如果可以走,且可以进行松弛,就直接松弛即可 
				dist[v] = dist[k] + 1;
		}
	}
	return ;
}

int main() {
	scanf ("%d %d", &n, &m); 
	for(int i = 1; i <= m; i++) {
		int x, y;
		scanf ("%d %d", &x, &y);
		s[x].push_back(y);
		t[y].push_back(x);
	}
	scanf ("%d %d", &S, &T);
	p[T] = true;
	dfs(T);
	for(int i = 1; i <= n; i++) { // 枚举每个点 
		int tot = 0;
		for(int j = 0; j < s[i].size(); j++) { // 枚举与这个点相连的边 
			if(flag[s[i][j]] == true) tot++; 
			// 更新一下有多少条边连接的点与终点联通 
		}
		if(tot == s[i].size() && tot != 0) p[i] = true;
		// 如果都与终点联通说明这个点可以走 
	}
//	printf("\n");
//	for(int i = 1; i <= n; i++) printf("%d %d\n", i, p[i]);
//	printf("\n");
	dijkstra();	
	if(dist[T] == 0x3f3f3f3f) printf("-1"); // 不能到达,输出-1 
	else printf("%d", dist[T]); // 输出到终点的最短路 
	return 0;
} 
posted @ 2020-10-24 11:36  STrAduts  阅读(186)  评论(0编辑  收藏  举报