死宅日志——图的遍历1

本文通过两个实例介绍了如何利用图的遍历(广度优先和深度优先)解决实际问题,包括确定犯罪团伙数量和寻找无向图的欧拉路径。通过具体的代码实现,展示了如何运用邻接矩阵和邻接表来处理图数据结构。
摘要由CSDN通过智能技术生成

图的遍历最常用的就是广度和深度,话不多说出招了。

【问题描述】
警察抓到了n个罪犯,警察根据经验知道他们属于不同的犯罪团伙,却不能判断有多少个团伙,但通过警察的审讯,知道其中的一些罪犯之间相互认识,已知同一犯罪团伙的成员之间直接或间接认识,有可能一个犯罪团伙只有一个人。请你根据已知罪犯之间的关系,确定犯罪团伙的数量。已知罪犯的编号从1至n。
【输入】
第一行:n(<1000,罪犯数量)。
第二行:m(<5000,关系数量)。
以下m行,每行两个数:i 和j,中间一个空格隔开,表示罪犯i和罪犯j相互认识。
【输出】
一个整数,犯罪团伙的数量。
Sample:
11
8
1 2
4 5
3 4
1 3
5 6
7 10
5 10
8 9

3
看下题,很简单嘛,不就是个求连通分量的吗。我这里用floodfill来防止重复。这个我用邻接矩阵来记录,当然这是因为数据量小。用邻接表也可以。
代码如下:
nt n,m,a[1010][1010]={0},f[1010]={0};//a为储存罪犯关系的矩阵,有关系的为1,f用来确认是否是独行侠
void recognized(int x)
{
int j;f[x]=1;
for(j=1+x;j<=n;j++)
if(a[x][j]==1){a[x][j]=0;a[j][x]=0;recognized(j);}
}
int main()
{
int x,y,i,j,s=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
a[x][y]=1;a[y][x]=1;//关系是相对的所以是无向图
}
for(i=1;i<n;i++){if(f[i]==0)s++;
for(j=i+1;j<=n;j++)
if(a[i][j]==1)recognized(i);}
printf("%d",s);
return 0;
}

农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。
John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过同一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(>=1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。
输入格式 Input Format
第1行: 一个整数F(1 <= F <= 1024),表示栅栏的数目
第2到F+1行: 每行两个整数i, j(1 <= i,j <= 500)表示这条栅栏连接i与j号顶点。
输出格式 Output Format 输出应当有F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。
input:
9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6
output:
1
2
3
4
2
5
4
6
5
7

这是个求欧拉路(把所有路都走一遍不重复)的题。所以我们要首先判断图中是否有欧拉路。对于一个无向图,如果它每个点的度都是偶数,那么它存在一条欧拉回路;如果有且仅有2个点的度为奇数,那么它存在一条欧拉路;如果超过2个点的度为奇数,那么它就不存在欧拉路了。
由于题目中说数据保证至少有1个解,所以一定存在欧拉路了。但是我们还要选一个点作为起点。如果没有点的度为奇数,那么任何一个点都能做起点。如果有2个奇点,那么就只能也这两个点之一为起点,另一个为终点。
int so[550][550] = {0}, f, s[550] = { 0 },p[1050],flag=0;
void dfs(int d, int count)// 深搜一条路走到黑
{
int i;
for (i = 1; i <= 500; i++)
if (so[d][i] > 0)
{
so[d][i]–;
so[i][d]–;
dfs(i, count + 1);
so[d][i]++;
so[i][d]++;
if (flag)break;
}
if (count ==1+f)flag = 1;
if (flag)p[count] = d;
}
int main()
{
int a, b, i;
scanf_s("%d", &f);//vs的安全开发生命周期(SDL)检查-.-
for (i = 1; i <= f; i++)
{
scanf_s("%d%d", &a, &b);
s[a]++;
s[b]++;
so[a][b]++;
so[b][a]++;
}
for (i = 1; i <= 500; i++)
{
if (s[i] % 2 != 0 && s[i] > 0)dfs(i, 1);
if (flag)break;
}
if(i>500)for (i = 1; i <= 500; i++)if(s[i] > 0)dfs(i, 1);
for (i = 1; i <= f + 1; i++)
printf("%d ", p[i]);
return 0;
}

小修是个几何迷。她有一天画了一个正n边形,并且将n个顶点用1,2,…,n这n个连续自然数随手编了一下号。然后她又画了一些不相交的对角线

她把所有的边和对角线都写在一张纸上。对上图,她写了:(1,3), (3,2), (2,4), (4,5), (5,1), (1,4), (3,4)。
过了几个星期,她无意中发现了这张写着字的纸,可是怎么也找不着那个几何图形了。她很想把n边形的编号复原,可是试了一天也没弄出来。你能帮助她吗?
输入格式 Input Format 第一行n(n<=50)。
下面的若干行每行两个数a, b。表示纸上写着(a,b)。
输出格式 Output Format 仅一行,按顺序依次输出顶点的编号。对于上面的例子,你的输出应该是1 3 2 4 5。
1 5 4 2 3也是符合题目要求的。两者区别只是逆时针和顺时针而已。
但是你的输出只能是1 3 2 4 5!也就是说你必须把两个符合要求的输出比较大小(先比较第一位;第一位相等就比较第二位;第二位相等……以此类推),你的输出应该是较小者!(这是为了评测的方便)
sample:
5
1 3
3 2
2 4
4 5
5 1
1 4
3 4

1 3 2 4 5
上一道题我用邻接矩阵写的这道我用邻接表来写:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<conio.h>
struct aa
{
int e, n;//e是终点 n是下条边的编号
}p[2510];
int m,l[60] = { 0 }, a[60], f[60] = {0},find=0,t=0;//l是i顶点的一条边边的编号 a是答案 f去重
void dfs(int node,int count)//经历count个node
{
f[node] = 1;
int i = l[node];
if (count == m && p[i].e == 1) { find = 1; t = 1; }
while (1)//遍历每条边
{
if (f[p[i].e]== 0&&((a[count]>p[i].e&&find)||!find)) //要输出最小的那个
{
t = 0;
dfs(p[i].e, count + 1);
f[p[i].e]=0;
if (t)a[count+1] = p[i].e;
}
if (p[i].n== 0)break;
else i=p[i].n;
}
if (find)a[count] = node;
}//下面no problemw
int main()
{
int i=0,x,y;
scanf_s("%d",&m);
while (i/2<m+m-3)//因为都不相交循环m+m-3次
{
scanf_s("%d%d", &x, &y);//邻接表
p[++i].n = l[x];//无向图要录入两次
p[i].e = y;
l[x] = i;
p[++i].n = l[y];
p[i].e =x;
l[y] = i;
}
dfs(1,1);
for (i = 1; i <= m; i++)printf_s("%d ",a[i]);
system(“pause”);
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值