A - 数据结构实验之图论二:图的深度遍历
Description
请定一个无向图,顶点编号从0到n-1,用深度优先搜索(DFS),遍历并输出。遍历时,先遍历节点编号小的。
Input
输入第一行为整数n(0 < n < 100),表示数据的组数。 对于每组数据,第一行是两个整数k,m(0 < k < 100,0 < m < k*k),表示有m条边,k个顶点。 下面的m行,每行是空格隔开的两个整数u,v,表示一条连接u,v顶点的无向边。
Output
输出有n行,对应n组输出,每行为用空格隔开的k个整数,对应一组数据,表示DFS的遍历结果。
Sample
Input
1 4 4 0 1 0 2 0 3 2 3
Output
0 1 2 3
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int eps = 1e-8;// 一个极小值
const int N = 1e5 + 100;
const int M = 3e3 + 100;
const int INF = 0x3f3f3f3f;
const LL LINF = 0x3f3f3f3f3f3f3f3f;
#define PI acos(-1.0)
#define Equ(a, b) ((fabs((a) - (b))) < (eps))
int mp[110][110];
int vis[110];
int ans[110];
int cnt;
int k, m;
void dfs(int s){
vis[s] = 1;
ans[cnt++] = s;
for(int i = 0; i < k; i++){
if(mp[s][i] && !vis[i]){
dfs(i);
}
}
}
int main()
{
int t;
cin >> t;
while(t--){
cnt = 0;
memset(vis, 0, sizeof(vis));
memset(mp, 0, sizeof(mp));
memset(ans, 0, sizeof(ans));
cin >> k >> m;
for(int i = 0; i < m; i++){
int u, v;
cin >> u >> v;
mp[u][v] = mp[v][u] = 1;
}
dfs(0);
for(int i = 0; i < cnt; i++){
if(i == cnt - 1) cout << ans[i] << endl;
else cout << ans[i] << " ";
}
}
return 0;
}
B - 数据结构实验之图论四:迷宫探索
Description
有一个地下迷宫,它的通道都是直的,而通道所有交叉点(包括通道的端点)上都有一盏灯和一个开关;请问如何从某个起点开始在迷宫中点亮所有的灯并回到起点?
Input
连续T组数据输入,每组数据第一行给出三个正整数,分别表示地下迷宫的结点数N(1 < N <= 1000)、边数M(M <= 3000)和起始结点编号S,随后M行对应M条边,每行给出一对正整数,表示一条边相关联的两个顶点的编号。
Output
若可以点亮所有结点的灯,则输出从S开始并以S结束的序列,序列中相邻的顶点一定有边,否则只输出部分点亮的灯的结点序列,最后输出0,表示此迷宫不是连通图。
访问顶点时约定以编号小的结点优先的次序访问,点亮所有可以点亮的灯后,以原路返回的方式回到起点。
Sample
Input
1 6 8 1 1 2 2 3 3 4 4 5 5 6 6 4 3 6 1 5
Output
1 2 3 4 5 6 5 4 3 2 1
Hint
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3 + 100;
const int M = 2e2 + 100;
const int MAXN = 0x3f3f3f3f;
const LL LMANX = 0x3f3f3f3f3f3f3f3f;
#define PI 3.14159265358979323846
int t, n, m, s;
int u, v;
int cnt = 0;
int mp[N][N];
int vis[N], ans[N];
void dfs(int start){
vis[start] = 1;
ans[++cnt] = start;// 注意:这里数组下标从1开始
for(int i = 1; i <= m; i++){
if(vis[i] == 0 && mp[start][i] != 0){
// 该点没遍历过且从顶点到该点有边
dfs(i);
ans[++cnt] = start;// 倒着存储进去(按原路返回)
}
}
}
int main()
{
scanf("%d", &t);
while(t--){
cnt = 0;
memset(vis, 0, sizeof(vis));
memset(mp, 0, sizeof(mp));
scanf("%d%d%d", &n, &m, &s);
for(int i = 1; i <= m; i++){
scanf("%d%d", &u, &v);
mp[u][v] = mp[v][u] = 1;
}
dfs(s);
if(cnt == n * 2 - 1){
for(int i = 1; i <= cnt; i++){
printf("%d%c", ans[i], i == cnt ? '\n' : ' ');
}
}
else{
for(int i = 1; i <= cnt; i++){
printf("%d ", ans[i]);
}
printf("0\n");
}
}
return 0;
}
C - 数据结构实验之栈与队列十:走迷宫
Description
一个由n * m 个格子组成的迷宫,起点是(1, 1), 终点是(n, m),每次可以向上下左右四个方向任意走一步,并且有些格子是不能走动,求从起点到终点经过每个格子至多一次的走法数。
Input
第一行一个整数T 表示有T 组测试数据。(T <= 110)
对于每组测试数据:
第一行两个整数n, m,表示迷宫有n * m 个格子。(1 <= n, m <= 6, (n, m) !=(1, 1) ) 接下来n 行,每行m 个数。其中第i 行第j 个数是0 表示第i 行第j 个格子可以走,否则是1 表示这个格子不能走,输入保证起点和终点都是都是可以走的。
任意两组测试数据间用一个空行分开。
Output
对于每组测试数据,输出一个整数R,表示有R 种走法。
Sample
Input
3 2 2 0 1 0 0 2 2 0 1 1 0 2 3 0 0 0 0 0 0
Output
1 0 4
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 15;
const int M = 2e2 + 100;
const int MAXN = 0x3f3f3f3f;
const LL LMANX = 0x3f3f3f3f3f3f3f3f;
#define PI 3.14159265358979323846
int n, m;
int mp[N][N];
int vis[N][N];
int r = 0;
void dfs(int x, int y){// x代表行,y代表列
if(x < 1 || x > n || y < 1 || y > m || mp[x][y])// 越界或遇到障碍
return ;// 跳出if语句
if(x == n && y == m){// 到终点
r++;
return ;
}
if(vis[x][y] == 0){
vis[x][y] = 1;
// 按照顺时针的方向递归 右->下->左->上
dfs(x, y + 1);
dfs(x + 1, y);
dfs(x, y - 1);
dfs(x - 1, y);
vis[x][y] = 0;// 尝试结束,取消这个点的标记!!这样才能再次从(x,y)出发
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
memset(mp, 0, sizeof(mp));
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){// 题目规定地图从(1,1)开始
for(int j = 1; j <= m; j++){
scanf("%d", &mp[i][j]);
}
}
r = 0;
dfs(1, 1);
printf("%d\n", r);
}
return 0;
}
D - 数据结构实验之图论一:基于邻接矩阵的广度优先搜索遍历
Description
给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列。(同一个结点的同层邻接点,节点编号小的优先遍历)
Input
输入第一行为整数n(0< n <100),表示数据的组数。
对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,0< t<k),表示有m条边,k个顶点,t为遍历的起始顶点。
下面的m行,每行是空格隔开的两个整数u,v,表示一条连接u,v顶点的无向边。
Output
输出有n行,对应n组输出,每行为用空格隔开的k个整数,对应一组数据,表示BFS的遍历结果。
Sample
Input
1 6 7 0 0 3 0 4 1 4 1 5 2 3 2 4 3 5
Output
0 3 4 2 5 1
Hint
以邻接矩阵作为存储结构。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e2 + 7;
const int M = 2e2 + 100;
const int MAXN = 0x3f3f3f3f;
const LL LMANX = 0x3f3f3f3f3f3f3f3f;
#define PI 3.14159265358979323846
int k, m, t;
int u, v;
int cnt = 0;
int ans[N];
int mp[N][N], vis[N];// vis数组保存该点是否被遍历过
void bfs(int s){
int x;
queue<int> q;
vis[s] = 1;// 第一个点先标记上
q.push(s);
ans[cnt++] = s;
// 队列--先进先出,后进后出
while(!q.empty()){
x = q.front();
q.pop();
for(int i = 0; i < k; i++){
if(mp[x][i] == 1 && vis[i] == 0){
// 如果该边存在且没遍历过
vis[i] = 1;// 标记上
q.push(i);
ans[cnt++] = i;
}
}
}
printf("\n");
}
int main()
{
int n;
scanf("%d", &n);
while(n--){
cnt = 0;
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));// 清空不能少
scanf("%d%d%d", &k, &m, &t);
for(int i = 0; i < m; i++){
scanf("%d%d", &u, &v);
mp[u][v] = mp[v][u] = 1;
}
bfs(t);
for(int i = 0; i < cnt; i++){
if(i == n - 1) printf("%d\n", ans[i]);
else printf("%d ", ans[i]);
}
}
return 0;
}
E - 数据结构实验之图论二:基于邻接表的广度优先搜索遍历
Description
给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列。(同一个结点的同层邻接点,节点编号小的优先遍历)
Input
输入第一行为整数n(0< n <100),表示数据的组数。
对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,0< t<k),表示有m条边,k个顶点,t为遍历的起始顶点。
下面的m行,每行是空格隔开的两个整数u,v,表示一条连接u,v顶点的无向边。
Output
输出有n行,对应n组输出,每行为用空格隔开的k个整数,对应一组数据,表示BFS的遍历结果。
Sample
Input
1 6 7 0 0 3 0 4 1 4 1 5 2 3 2 4 3 5
Output
0 3 4 2 5 1
Hint
用邻接表存储。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2 + 100;
const int MAXN = 0x3f3f3f3f;
typedef long long ll;
const ll LMANX = 0x3f3f3f3f3f3f3f3f;
vector<int> g[N];// 用邻接表存储
int n, m;
int vis[N];// 标记点,判断顶点是否遍历过
vector<int> ans;
// 广度优先搜索--用队列
void bfs(int s){
queue<int> q;
q.push(s);// 把起点放进去
vis[s] = 1;// 表明这个点已经走过了
while(q.size()){// 只要没有遍历完
int u = q.front();// 取出头部的点
q.pop();
ans.push_back(u);
for(int v: g[u]){
if(vis[v]) continue;// 如果已经走过这个点就跳过
vis[v] = 1;
q.push(v);
}
}
}
int main()
{
ios::sync_with_stdio(0);
int t;
cin >> t;
while(t--){
int s;
memset(vis, 0, sizeof vis);
ans.clear();
cin >> n >> m >> s;
for(int i = 0; i < n; i++) g[i].clear();// 最多只用到n
for(int i = 1; i <= m; i++){
int u, v;
cin >> u >> v;
g[u].push_back(v), g[v].push_back(u);// 无向边
}
//for(int i = 0; i < n; i++) sort(g[i].begin(), g[i].end());
// 由于邻接表是按照输入顺序存储的,而本题要求:
// 同一个结点的同层邻接点,节点编号小的优先遍历
// 所以要先排序
bfs(s);// 本题对于起点有规定
for(int i: ans){
cout << i << " ";
}
}
return 0;
}
**常用的是用递归实现dfs,用队列实现bfs。
F - 数据结构实验之图论三:判断可达性
Description
在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫。在他们所在的地域,有n个隘口,编号为1..n,某些隘口之间是有通道连接的。其中近卫军团在1号隘口,天灾军团在n号隘口。某一天,天灾军团的领袖巫妖王决定派兵攻打近卫军团,天灾军团的部队如此庞大,甚至可以填江过河。但是巫妖王不想付出不必要的代价,他想知道在不修建任何通道的前提下,部队是否可以通过隘口及其相关通道到达近卫军团展开攻击。由于n的值比较大(n<=1000),于是巫妖王找到了擅长编程的你 =_=,请你帮他解决这个问题,否则就把你吃掉变成他的魔法。为了拯救自己,赶紧想办法吧。
Input
输入包含多组,每组格式如下。
第一行包含两个整数n,m(分别代表n个隘口,这些隘口之间有m个通道)。
下面m行每行包含两个整数a,b;表示从a出发有一条通道到达b隘口(注意:通道是单向的)。
Output
如果天灾军团可以不修建任何通道就到达1号隘口,那么输出YES,否则输出NO。
Sample
Input
2 1 1 2 2 1 2 1
Output
NO YES
bfs:(dfs当然也可以)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3 + 100;
const int M = 2e2 + 100;
const int MAXN = 0x3f3f3f3f;
const LL LMANX = 0x3f3f3f3f3f3f3f3f;
#define PI 3.14159265358979323846
int mp[N][N];
int vis[N];
int n, m, a, b;
int flag;
void bfs(int x){
int top;
vis[x] = 1;
queue<int>q;
q.push(x);
while(!q.empty()){
top = q.front();
q.pop();
if(top == 1) flag = 1;// 如果能到达
else{
for(int i = 1; i <= n; i++){
if(mp[top][i] && !vis[i]){// 边存在且点没遍历过
vis[i] = 1;
q.push(i);
}
}
}
}
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF){
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));
for(int i = 0; i < m; i++){
scanf("%d%d", &a, &b);
mp[a][b] = 1;
}
flag = 0;
bfs(n);
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
G - 数据结构实验之图论五:从起始点到目标点的最短步数(BFS)
Description
在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫。在他们所在的地域,有n个隘口,编号为1..n,某些隘口之间是有通道连接的。其中近卫军团在1号隘口,天灾军团在n号隘口。某一天,天灾军团的领袖巫妖王决定派兵攻打近卫军团,天灾军团的部队如此庞大,甚至可以填江过河。但是巫妖王不想付出不必要的代价,他想知道在不修建任何通道的前提下,部队是否可以通过隘口及其相关通道到达近卫军团展开攻击;如果可以的话,最少需要经过多少通道。由于n的值比较大(n<=1000),于是巫妖王找到了擅长编程的你 =_=,请你帮他解决这个问题,否则就把你吃掉变成他的魔法。为了拯救自己,赶紧想办法吧。
Input
输入包含多组,每组格式如下。
第一行包含两个整数n,m(分别代表n个隘口,这些隘口之间有m个通道)。
下面m行每行包含两个整数a,b;表示从a出发有一条通道到达b隘口(注意:通道是单向的)。
Output
如果天灾军团可以不修建任何通道就到达1号隘口,那么输出最少经过多少通道,否则输出NO。
Sample
Input
2 1 1 2 2 1 2 1
Output
NO 1
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3 + 100;
const int M = 2e2 + 100;
const int MAXN = 0x3f3f3f3f;
const LL LMANX = 0x3f3f3f3f3f3f3f3f;
#define PI 3.14159265358979323846
int n, m;
int a, b;
int vis[N], step[N];// step[]用来存储到达每个隘口所用的步数
int mp[N][N];
int cnt = 0;
void bfs(int x){
int top;
vis[x] = 1;
queue<int>q;
q.push(x);
step[x] = 0;
while(!q.empty()){
top = q.front();
q.pop();
for(int i = 1; i <= n; i++){
if(mp[top][i] && !vis[i]){
q.push(i);
step[i] = step[top] + 1;
vis[i] = 1;
}
}
}
}
int main()
{
while(~scanf("%d%d", &n, &m)){
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));
memset(step, 0, sizeof(step));
for(int i = 1; i <= m; i++){
scanf("%d%d", &a, &b);
mp[a][b] = 1;// 单向的
}
cnt = 0;
bfs(n);
if(vis[1]) printf("%d\n", step[1]);
else printf("NO\n");
}
return 0;
}
H - 找朋友
Description
X,作为户外运动的忠实爱好者,总是不想呆在家里。现在,他想把死宅Y从家里拉出来。问从X的家到Y的家的最短时间是多少。
为了简化问题,我们把地图抽象为n*m的矩阵,行编号从上到下为1 到 n,列编号从左到右为1 到 m。矩阵中’X’表示X所在的初始坐标,’Y’表示Y的位置 , ’#’表示当前位置不能走,’ * ’表示当前位置可以通行。X每次只能向上下左右的相邻的 ’*’ 移动,每移动一次耗时1秒。
Input
多组输入。每组测试数据首先输入两个整数n,m(1<= n ,m<=15 )表示地图大小。接下来的n 行,每行m个字符。保证输入数据合法。
Output
若X可以到达Y的家,输出最少时间,否则输出 -1。
Sample
Input
3 3 X#Y *** #*# 3 3 X#Y *#* #*#
Output
4 -1
Hint
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 20;
const int M = 2e2 + 100;
const int MAXN = 0x3f3f3f3f;
const LL LMANX = 0x3f3f3f3f3f3f3f3f;
#define PI 3.14159265358979323846
char mp[N][N];
int vis[N][N];
int X1, Y1;
int n, m;
int Next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
struct node{
int x, y, step;// 这是每个点都具备的三个状态(学废了T_T)
}a, b;// a代表一个点,b代表该点的下一个点
void bfs(int x, int y){
a.x = x; a.y = y;
a.step = 0;
queue<node> q;
q.push(a);
vis[x][y] = 1;
int k;
while(!q.empty()){
a = q.front();
q.pop();
if(mp[a.x][a.y] == 'Y'){
printf("%d\n", a.step);
return ;
}
// 枚举四种走法
for(k = 0; k < 4; k++){
b.x = a.x + Next[k][0];
b.y = a.y + Next[k][1];
if(b.x >= 0 && b.x < n && b.y >= 0 && b.y < m && mp[b.x][b.y] != '#' && !vis[b.x][b.y]){
vis[b.x][b.y] = 1;
b.step = a.step + 1;// 每走一步花费一秒,所以步数就是时间啦
q.push(b);
}
}
}
printf("-1\n");
}
int main()
{
while(~scanf("%d%d", &n, &m)){
//getchar();
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));
for(int i = 0; i < n; i++){
scanf("%s", mp[i]);
for(int j = 0; j < m; j++){
if(mp[i][j] == 'X'){
X1 = i; Y1 = j;
}
}
}
/*这种方式也行
int i, j;
for(i = 0; i < n; i++){
for(j = 0; j < m; j++){
if(mp[i][j] == 'X'){
X1 = i; Y1 = j;
break;
}
}
if(j < m) break;
}
*/
bfs(X1, Y1);
}
return 0;
}
汤圆の拯救计划
Description
又到了汤圆星球一年一度的汤圆节了,但是大魔王却过来把汤圆公主抓走了Σ( ° △ °|||)︴
身为汤圆骑士的QAQ蒟蒻自然而然的肩负着拯救汤圆的使命
QAQ蒟蒻经历了千辛万苦(并没有)之后,来到了大魔王的城堡,根据情报,汤圆公主就被大魔王放在城堡内,然后QAQ蒟蒻发现自己是一个路
痴,所幸的是他拿到了大魔王的城堡的地图,而且在这上面标注了自己和汤圆公主的位置,那么问题来了,聪明的你能帮他计算出需要多少单位
的时间来赶到汤圆公主的位置吗?
Ps:QAQ蒟蒻每一次都可以移动到相邻的非墙的格子中,每次移动都要花费1个单位的时间
有公共边的格子定义为相邻
Input
一开始为一个整数T代表一共有T组数据
每组测试数据的第一行有两个整数n,m (2<=n,m<=300)
接下来的n行m列为大魔王的迷宫,其中
’#’为墙壁,‘_‘为地面
A代表QAQ蒟蒻,O代表汤圆公主:
Output
一组数据输出一个整数代表从QAQ蒟蒻到汤圆的位置的最短时间
如果QAQ蒟蒻不能到达汤圆的位置,输出-1
Sample
Input
2 3 3 __A _## __O 2 2 A# #O
Output
6 -1
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int eps = 1e-8;// 一个极小值
const int N = 3e2 + 100;
const int M = 3e3 + 100;
const int INF = 0x3f3f3f3f;
const LL LINF = 0x3f3f3f3f3f3f3f3f;
#define PI acos(-1.0)
#define Equ(a, b) ((fabs((a) - (b))) < (eps))
int t, n, m;
int sx, sy;
int point[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
char mp[N][N];
int vis[N][N];
struct arte{
int x, y;
int cost;
};// 每个点具有的三个属性
void bfs(int xx, int yy){
vis[xx][yy] = 1;
arte p;
queue<arte>q;
p.x = xx;
p.y = yy;
p.cost = 0;
q.push(p);
while(!q.empty()){
arte top = q.front();
q.pop();
if(mp[top.x][top.y] == 'O'){
cout << top.cost << endl;
return ;
}
for(int i = 0; i < 4; i++){
p.x = top.x + point[i][0];
p.y = top.y + point[i][1];
if(p.x >= 0 && p.x < n && p.y >= 0 && p.y < m && !vis[p.x][p.y] && mp[p.x][p.y] != '#'){
vis[p.x][p.y] = 1;
p.cost = top.cost + 1;
q.push(p);
}
}
}
cout << "-1" << endl;
}
int main()
{
scanf("%d", &t);
while(t--){
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin >> mp[i][j];
if(mp[i][j] == 'A') sx = i, sy = j;
}
}
bfs(sx, sy);
}
return 0;
}