图论
DFS: 从一个分支遍历到结束 沿一条分支一直搜索实现方式:递归
基本框架:
DFS(int x, int y, int z)
{
for(遍历分支)
dfs(a,b,c)
}
*vector 链式存储 可迅速插入删除
example:
一.变形课
Harry已经将他所会的所有咒语都列成了一个表,他想让你帮忙计算一下他是否能完成老师的作业,将一个B(ball)变成一个M(Mouse),你知道,如果他自己不能完成的话,他就只好向Hermione请教,并且被迫听一大堆好好学习的道理.
so soon river goes them got moon begin big 0
Yes.
Harry 可以念这个咒语:"big-got-them".
代码如下
#include <iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
vector<int> e[30];
bool vis[30];
bool flag;
int i;
void dfs(int x)
{
vis[x] = 1;
if(x == 12)
{
flag = 1;
return; //return 可以看作跳出了调用的这个函数,但只是跳出了当前的,要将所有嵌套的都跳出,用exit
}
//return true;
for(auto &v : e[x])
{
if(!vis[v])
dfs(v);
}
// return false;
}
int main()
{
string str;
while(cin>>str)
{
flag = 0;
memset(vis, 0, sizeof(vis)); //对vis清零 用0替换并返回vis
for(i = 0; i < 30; i++) //26个字母
e[i].clear(); //对容器清零了
while(str[0] != '0')
{
int len = str.length();
int be = (int)(str[0] - 'a');
int en = (int)(str[len - 1] - 'a');
e[be].push_back(en); //建立对应的关系,一个存一个
cin>>str;
}
dfs(1);
if(flag)
printf("Yes.\n");
else
printf("No.\n");
}
return 0;
}
1.对于这类题,首先要做的事是建图:将题目转化成树杈形状的图,分支一一明确
这道题中,每个单词的首字母和尾字母就是要找的分叉点
最上层的节点为b
第二层的节点是以b为首字母的单词的尾字母
第三层是以上一层尾字母为首字母的单词的尾字母
......
直到找到m为止
2.对于字母,字符这类相对来说难以进行运算的数据类型,将它们转化为整型的数字,来比较某种关系。
我们让a为0, b为1........m为12.........
3.然后就用容器把1中的关系建立起来:e[be].push_back(en);
4.以b为起始点,调用dfs函数
5.dfs函数
用for(auto &v : e[x])
{
if(!vis[v])
dfs(v);
}这种结构遍历e[x]中的元素,此时,x为第一个节点b
v依次代表了第二层的节点,不断的调用
*其中,为了防止重复调用同一个字母,设置一个判断函数vis:
如果x元素进入过dfs函数,就让vis[x] = 1;
只有当vis[x] != 1 的时候才调用。
直到 x == 12 时,说明已经找到了,停止调用dfs函数,让flag = 1 结束
*这里结束可以直接用return;
return 可以看作跳出了调用的这个函数,但只是跳出了当前的,要将所有嵌套的都跳出,用exit
6.还要注意:对于测试多组数据的题目,一定要记得清空数组和容器
清空数组 memset(vis, 0, sizeof(vis));
清空容器 for(i = 0; i < 30; i++) e[i].clear();
二.数油田
1 1 * 3 5 *@*@* **@** *@*@* 1 8 @@****@* 5 5 ****@ *@@*@ *@**@ @@@*@ @@**@ 0 0
0 1 2 2
代码如下:
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int d[8][2] = {-1,1, -1,0, -1,-1, 0,1, 0,-1, 1,1, 1,0, 1,-1};
bool vis[105][105];
char a[105][105];
int m, n;
bool check(int x, int y)
{
if(x >= 0 && y >= 0 && x < m && y < n && a[x][y] == '@' && !vis[x][y])
return true;
return false;
}
void dfs(int x, int y)
{
int x0, y0;
vis[x][y] = 1;
for(int h = 0; h < 8; h++)
{
x0 = x + d[h][0];
y0 = y + d[h][1];
if(check(x0, y0))
{
dfs(x0, y0);
}
}
}
int main()
{
int i, j, t;
while(scanf("%d %d", &m, &n) && m + n)
{
t = 0; //一定要初始化
memset(vis, 0, sizeof(vis));
memset(a, 0, sizeof(a));
for(i = 0; i < m; i++)
{
for(j = 0; j < n; j++)
{
cin>>a[i][j];
}
}
for(i = 0; i < m; i++)
{
for(j = 0; j < n; j++)
if(a[i][j] == '@' && !vis[i][j])
{
dfs(i, j);
t++;
}
}
printf("%d\n", t);
}
return 0;
}
1.输入字符型数组:cin>>a[i][j]; 用scanf会有点问题
2.这道题的建图:
起始点为第一层
起始点8个方向的是 @ 的点组成第二层
.........
直到找不到新的油田的位置为止
3.用一个数组来遍历方向
4.用一个check函数检查是否越界和重复
5.注意测试多组数据的时候一定要将计数器等常量初始化。
BFS:从起始点,探索周围的点,直到结束
实现方式:队列
基本框架:
BFS(int x, inty, int z)
BFS(int x, inty, int z)
{
queue<type> q;
while(!q.empty())
{
q.pop();
for(遍历四周)
if(满足条件)
q.push();
}
}
example:
一. strange lift
Here comes the problem: when you are on floor A,and you want to go to floor B,how many times at least he has to press the button "UP" or "DOWN"?
The first line contains three integers N ,A,B( 1 <= N,A,B <= 200) which describe above,The second line consist N integers k1,k2,....kn.
A single 0 indicate the end of the input.
5 1 5 3 3 1 2 5 0
3
代码如下:
#include <iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n, a, b, k[205];
bool vis[205];
struct lift
{
int f;
int s;
};
int bfs()
{
queue<lift> q;
q.push(lift{a, 0}); //结构体初始化的方式
int r = -1;
while(!q.empty())
{
lift m = q.front();
vis[m.f] = 1;
q.pop();
if(m.f == b)
{
r = m.s;
break;
}
int up = m.f + k[m.f - 1];
int down = m.f - k[m.f - 1];
if(up <= n && !vis[up])
q.push(lift{up, m.s + 1}); //都是在这个m结构体下+1;
if(down >= 1 && !vis[down])
q.push(lift{down, m.s + 1});
}
while(!q.empty()) q.pop(); //清空队列
return r;
}
int main()
{
int i;
while(scanf("%d", &n) && n != 0)
{
memset(vis, 0, sizeof(vis));
scanf("%d %d", &a, &b);
for(i = 0; i < n; i++)
scanf("%d", &k[i]);
printf("%d\n", bfs());
}
return 0;
}
1.结构体的使用:
因为要记录下走的步数,所以不能单纯的整型,而是要使用结构体
结构体初始化的方式 q.push(lift{a(第一个量), 0(第二个量)});
2.bfs函数
bfs函数主要就是队列,先让第一层进入队列,然后操作到第二层,再让所以第二层入队列
不断访问队列中的首元素,直到找到符合条件的元素。
3.清空队列 while(!q.empty()) q.pop();
二.pet
1 10 2 0 1 0 2 0 3 1 4 1 5 2 6 3 7 4 8 6 9
2
代码如下:
#include <iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
vector<int>e[100005];
int d;
struct pet
{
int loc;
int s;
};
int bfs()
{
int t = 0;
queue<pet>q;
for(auto &v : e[0]) //现让起始位置的进队列
{
q.push({v, 0});
}
while(!q.empty())
{
pet m = q.front();
q.pop();
if(m.s >= d) t++;
for(auto &w : e[m.loc])
{
q.push({w, m.s + 1});
}
}
return t;
}
int main()
{
int i, n, N, a, b, j;
scanf("%d", &N);
for(i = 0; i < N; i ++)
{
for(j = 0; j < 100005; j++)
e[j].clear();
scanf("%d %d", &n, &d);
for(j = 0; j < n - 1; j++)
{
scanf("%d %d", &a, &b);
//if(a != 0) h = j;
e[a].push_back(b);
}
printf("%d\n", bfs());
}
return 0;
}