chap 7 图
6-1 邻接矩阵存储图的深度优先遍历
https://pintia.cn/problem-sets/1581928194336620544/exam/problems/1581928194407923780
dfs深度优先搜索就是每次往当前点的子节点下面搜索,搜索到底了再返回继续搜索其他点。
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex) )//可以把这个函数看成访问当前点以及所有子节点 { Visited[V]=true;//先标记当前点为访问过 Visit(V);int i=0; for(i=0;i<Graph->Nv;i++) { if(Graph->G[V][i]==1)//当前V和i之间有边连接 { if(Visited[i]==false)//如果当前点没有被访问过 { DFS(Graph,i,Visit); } } } }
6-2 邻接表存储图的广度优先遍历
https://pintia.cn/problem-sets/1581928194336620544/exam/problems/1581928194407923781
bfs广度优先有点像树的层次遍历,它是一层一层的访问的,所以需要记录当前层的所有点,然后再从每个点拓展出新的一层,以队列的形式依次访问。
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) )
{
int que[1000];//用数组模拟一个队列
int head=0;int tail=0;//头节点和尾节点
que[tail]=S;tail++;
Visited[S]=true;//加入第一个点
struct AdjVNode* p;//第一个节点
while(head!=tail)
{
int v=que[head++];//取出头节点
Visit(v);//访问
p=Graph->G[v]. FirstEdge;
while(p!=NULL)
{
if(!Visited[p->AdjV])
{
que[tail++]=p->AdjV;//遍历当前节点连接的所有点入队
Visited[p->AdjV]=true;
}
p=p->Next;
}
}
}
7-1 畅通工程之局部最小花费问题
https://pintia.cn/problem-sets/1581928194336620544/exam/problems/1581928194407923782
这里需要用到离散数学中的最小生成树,畅通工程且最小花费,意味着要连接所有点且边权最小,刚好符合最小生成树的性质,如果当前路径已经修好了,那么边权记为0,否则记为维修费用,这样就可以找出最小维修费用。那么怎么判断两个点是不是在一个集合呢?
这里需要用到并查集
int F[101]; int find(int x) { if(x!=F[x]) return F[x]=find(F[x]); else return x; }
这里的F数组记录着当前点的最远父亲节点,只要两个点的父亲节点一样说明在一个集合。
那么就可以按照最小生成树原理来写代码了
#include<bits/stdc++.h>
using namespace std;
int F[101];
int find(int x)
{
if(x!=F[x])
return F[x]=find(F[x]);
else return x;
}
struct edge
{
int u;
int v;
int w;
};
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
vector<edge>e;
int main()
{
int n;
cin>>n;
for(int i=0;i<n*(n-1)/2;i++)
{
int u,v,w,d;
cin>>u>>v>>w>>d;
if(d==1)//修好了
{
e.push_back({u,v,0});
}
else
{
e.push_back({u,v,w});
}
}
sort(e.begin(),e.end(),cmp);//对边权排序
int sum=0;
for(int i=1;i<=n;i++)//并查集初始化
{
F[i]=i;
}
F[e[0].v]=e[0].u;
sum+=e[0].w;
int cnt=1;
for(int i=1;i<e.size();i++)
{
int a=find(e[i].u);
int b=find(e[i].v);
if(a==b)continue;
F[b]=a;//连接两个集合
cnt++;sum+=e[i].w;
}
cout<<sum<<endl;
}
7-2 畅通工程之最低成本建设问题
https://pintia.cn/problem-sets/1581928194336620544/exam/problems/1581928194407923783
这道题和上面那题差不多,但是这道题不能保证每个点都能到达,可以先跑一边kruskal,构建最小生成树,然后再遍历每个点,如果当前点没有被加入集合(如果每个点都能到达,那么只有一个点的父亲节点是自己),那么说明不能畅通。
#include<bits/stdc++.h>
using namespace std;
int F[1001];
int find(int x)
{
if(x!=F[x])
return F[x]=find(F[x]);
else return x;
}
struct edge
{
int u;
int v;
int w;
};
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
vector<edge>e;
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++)
{
int u,v,w,d;
cin>>u>>v>>w;
e.push_back({u,v,w});
}
sort(e.begin(),e.end(),cmp);
int sum=0;
for(int i=1;i<=n;i++)
{
F[i]=i;
}
for(int i=0;i<e.size();i++)
{
// if(cnt==n-1)break;
int a=find(e[i].u);
int b=find(e[i].v);
if(a==b)continue;
F[b]=a;
sum+=e[i].w;
}
int s=0;
for(int i=1;i<=n;i++)
{
if(F[i]==i)s++;//父亲节点是自己的点
}
if(s!=1)
cout<<"Impossible"<<endl;
else
cout<<sum<<endl;
}