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;
}