浙大2020年Mooc数据结构笔记–第八讲 图(下)
〇、前言
- 这几天开始跟着学数据结构,鉴于当初数据结构实在学的太弱,加之这项工作算是为大家之后复习、机试铺路。确实是一个迫切需要做的大规模工作。行胜于言,虽然从第二篇开始,坚持下去。

- 此文部分为自己写,部分参考网上内容。提前说一下哈。期待批评指正
一、最小生成树



二、拓扑排序


三、课后题

1、7-10 公路村村通(30 分)

输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
/***************2019.5.4-9:20-12:00 12:40-13:00***********/
//08-图7 公路村村通 3h
#include<cstdio>
#define MaxSize 1008
typedef struct ENode *Edge;
struct ENode{
int V1,V2;
int weight;
}E[MaxSize*3],tmp,X;
int Home[MaxSize];
void Insert(int i){
tmp=E[i];
for(i;E[i/2].weight>tmp.weight;i=i/2){
E[i]=E[i/2];
}
E[i]=tmp;
}
void Delete(int end){
if(end==0) return;
tmp=E[end];
int Parent,Child;
for(Parent=1;Parent*2<=end;Parent=Child){
Child=Parent*2;
if(Child!=end&&E[Child].weight>E[Child+1].weight) Child+=1;
if(tmp.weight<=E[Child].weight) break;
else E[Parent]=E[Child];
}
E[Parent]=tmp;
}
int Together(int V1,int V2){
int P1=V1;
int P2=V2;
while(Home[P1]>0) P1=Home[P1];
while(Home[P2]>0) P2=Home[P2];
if(P1==P2) return 1;
else {
if(Home[P1]<Home[P2]){
Home[P1]=Home[P1]+Home[P2];
Home[P2]=P1;
}
else{
Home[P2]=Home[P1]+Home[P2];
Home[P1]=P2;
}
return 0;
}
}
int main(){
int N,M;
int i;
scanf("%d%d",&N,&M);
E[0].weight=-1;
for(i=1;i<=M;i++){
scanf("%d%d%d",&E[i].V1,&E[i].V2,&E[i].weight);
Insert(i);
}
int end=M;
long long Cost=0;
int Enum=0;
for(i=1;i<=N;i++) Home[i]=-1;
while(1){
X=E[1];
Delete(end--);
if(end<0) break;
/* if(!Visited[X.V1]||!Visited[X.V2]){//这里不能保证v1和v2有边 v3和v4有边 然后连通这两个顶点
Visited[X.V1]=1;
Visited[X.V2]=1;
Cost+=X.weight;
Enum++;
if(Enum==N-1) break;
}*/
if(!Together(X.V1,X.V2)){//在一起跳过,不在一起在一起
Cost+=X.weight;
Enum++;
if(Enum==N-1) break;
}
}
if(Enum==N-1) printf("%lld\n",Cost);
else printf("-1\n");
return 0;
}
2、7-12 How Long Does It Take(30分)

9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
Sample Output 1:
18
这道题是拓扑排序的变形,一个项目的每个节点记录了该节点的最早完成时间,因为要完成该节点之后的事件,必须完成该节点所有前驱节点的事件,所以该节点最早完成时间等于该节点所有前驱节点的最早完成时间+前驱节点到该节点所需的时间(边权重)中的最大值。具体代码实现如下:
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 105 /* 最大顶点数设为100 */
typedef int Vertex; /* 用顶点下标表示顶点,为整型 */
typedef int WeightType; /* 边的权值设为整型 */
typedef int DataType; /* 顶点存储的数据类型设为整型 */
/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode{
Vertex V1, V2; /* 有向边<V1, V2> */
WeightType Weight; /* 权重 */
};
typedef PtrToENode Edge;
/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV; /* 邻接点下标 */
WeightType Weight; /* 边权重 */
PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};
/* 顶点表头结点的定义 */
typedef struct Vnode{
PtrToAdjVNode FirstEdge;/* 边表头指针 */
DataType Earliest; /* 存顶点的数据 */
} AdjList[MaxVertexNum]; /* AdjList是邻接表类型 */
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
AdjList G; /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
LGraph BuildGraph();
bool TopSort( LGraph Graph, Vertex TopOrder[] );
int main()
{
LGraph graph;
Vertex TopOrder[MaxVertexNum];
int i,max = 0; //所有检查点最早完成时间的最大值
graph = BuildGraph();
if( !TopSort(graph,TopOrder) )
printf("Impossible\n");
else{
for(i=0; i<graph->Nv; i++){
if(graph->G[i].Earliest > max){
max = graph->G[i].Earliest;
}
}
printf("%d\n",max);
}
return 0;
}
LGraph CreateGraph( int VertexNum )
{ /* 初始化一个有VertexNum个顶点但没有边的图 */
Vertex V;
LGraph Graph;
Graph = (LGraph)malloc( sizeof(struct GNode) ); /* 建立图 */
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化邻接表头指针 */
/* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
for (V=0; V<Graph->Nv; V++)
Graph->G[V].FirstEdge = NULL;
return Graph;
}
void InsertEdge( LGraph Graph, Edge E )
{
PtrToAdjVNode NewNode;
/* 插入边 <V1, V2> */
/* 为V2建立新的邻接点 */
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
/* 将V2插入V1的表头 */
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
}
LGraph BuildGraph()
{
LGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf("%d", &Nv); /* 读入顶点个数 */
Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */
scanf("%d", &(Graph->Ne)); /* 读入边数 */
if ( Graph->Ne != 0 ) { /* 如果有边 */
E = (Edge)malloc( sizeof(struct ENode) ); /* 建立边结点 */
/* 读入边,格式为"起点 终点 权重",插入邻接表中 */
for (i=0; i<Graph->Ne; i++) {
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
InsertEdge( Graph, E );
}
}
for (V=0; V<Graph->Nv; V++)
Graph->G[V].Earliest = 0; //初始化每个顶点的最早完成时间为0天
return Graph;
}
/* 邻接表存储 - 拓扑排序算法 */
bool TopSort( LGraph Graph, Vertex TopOrder[] )
{ /* 对Graph进行拓扑排序, TopOrder[]顺序存储排序后的顶点下标 */
int Indegree[MaxVertexNum], cnt;
Vertex V;
PtrToAdjVNode W;
Vertex queue[MaxVertexNum]; //结点队列
int head = 0,tail = 0; //队列头尾指针
/* 初始化Indegree[] */
for (V=0; V<Graph->Nv; V++)
Indegree[V] = 0;
/* 遍历图,得到Indegree[] */
for (V=0; V<Graph->Nv; V++)
for (W=Graph->G[V].FirstEdge; W; W=W->Next)
Indegree[W->AdjV]++; /* 对有向边<V, W->AdjV>累计终点的入度 */
/* 将所有入度为0的顶点入列 */
for (V=0; V<Graph->Nv; V++)
if ( Indegree[V]==0 )
queue[tail++] = V;
/* 下面进入拓扑排序 */
cnt = 0;
while( head < tail ){
V = queue[head++]; /* 弹出一个入度为0的顶点 */
TopOrder[cnt++] = V; /* 将之存为结果序列的下一个元素 */
/* 对V的每个邻接点W->AdjV */
for ( W=Graph->G[V].FirstEdge; W; W=W->Next ){
/*W->Adjv结点处的最早完成时间为(每个前面必须完成结点的最早完成时间+边的权重)的最大值*/
if( Graph->G[V].Earliest+W->Weight > Graph->G[W->AdjV].Earliest)
Graph->G[W->AdjV].Earliest = Graph->G[V].Earliest+W->Weight;
if ( --Indegree[W->AdjV] == 0 )/* 若删除V使得W->AdjV入度为0 */
queue[tail++] = W->AdjV; /* 则该顶点入列 */
}
} /* while结束*/
if ( cnt != Graph->Nv )
return false; /* 说明图中有回路, 返回不成功标志 */
else
return true;
}
3、7-11 关键活动(30分)

输入样例:
7 8
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2
输出样例:
17
1->2
2->4
4->6
6->7
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<queue>
#define inf 999999
using namespace std;
struct vertex{
int earliest;
int latest;
}v[105];
struct node{
int x,y,rank;
friend bool operator < (node a, node b)
{
if(a.x==b.x)
{
return a.rank<b.rank;
}
else return a.x>b.x;
}
}np,na;
queue<int>q;
priority_queue<node>q2;
int c[105][105],d[105][105],In[105] = {0},Out[105] = {0};
int main()
{
int n,m,a,b,t,i,j,x,cnt = 0,max = 0,flag = 0;
scanf("%d %d",&n,&m);
for(i = 1; i <= 105; i++)
{
v[i].earliest = 0;
v[i].latest = inf;
}
for(i = 1; i <= n; i++)
{
for(j = 1; j<= n; j++)
{
c[i][j] = inf;
}
}
for(i = 0; i < m; i++)
{
scanf("%d %d %d",&a,&b,&t);
c[a][b] = t;
np.x = a;
np.y = b;
np.rank = ++cnt;
q2.push(np);
In[b]++;
Out[a]++;
}
cnt = 0;
for(i = 1; i <= n; i++)
{
if(In[i]==0)
{
v[i].earliest = 0;
q.push(i);
}
}
while(!q.empty())
{
x = q.front();
q.pop();
cnt++;
if(v[x].earliest>=max)
{
max = v[x].earliest;
}
for(i = 1; i <= n; i++)
{
if(c[x][i]!=inf)
{
if(v[i].earliest<=v[x].earliest+c[x][i])
{
v[i].earliest = v[x].earliest+c[x][i];
}
if(--In[i]==0)
{
q.push(i);
}
}
}
}
if(cnt!=n) printf("0\n");
else
{
flag = 1;
printf("%d\n",max);
}
if(flag)
{
for(i = 1; i <= n; i++)
{
if(Out[i]==0)
{
v[i].latest = max;
q.push(i);
}
}
while(!q.empty())
{
x = q.front();
q.pop();
for(i = 1; i <= n; i++)
{
if(c[i][x]!=inf)
{
if(v[i].latest >= v[x].latest-c[i][x])
{
v[i].latest = v[x].latest-c[i][x];
}
if(--Out[i]==0) q.push(i);
}
}
}
while(!q2.empty())
{
na = q2.top();
d[na.x][na.y] = v[na.y].latest-v[na.x].earliest-c[na.x][na.y];
if(d[na.x][na.y]==0)
{
printf("%d->%d\n",na.x,na.y);
}
q2.pop();
}
}
}
本文档提供了浙江大学2020年数据结构课程的Mooc笔记,重点讲解了图论中的最小生成树、拓扑排序等概念,并通过实际编程题目加深理解,包括公路村村通、HowLongDoesItTake和关键活动等问题。
772

被折叠的 条评论
为什么被折叠?



