浙江大学MOOC数据结构-陈越、何钦铭 编程练习题(第八讲)
编程说明
编程环境:平台运行
编程语言:C
第一题代码
参考自https://www.cnblogs.com/minesweeper/p/6114804.html
Prim
#include <stdio.h>
#include <stdlib.h>
#define INF 10000
int N, M;
int TotalCost = 0;
int Cost_Matrix[1001][1001];
int dist[1001];
void Init();
int Prim();
int Find_Min();
int main()
{
Init();
printf("%d", Prim());
return 0;
}
void Init()
{
int v1, v2, cost;
//获取城镇数目、道路条数
scanf("%d %d", &N, &M);
//初始化邻接矩阵
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
{
if(i != j)
Cost_Matrix[i][j] = INF;
else
Cost_Matrix[i][j] = 0;
}
//获取邻接矩阵的权值
for(int i = 1; i <= M; i++)
{
scanf("%d %d %d", &v1, &v2, &cost);
Cost_Matrix[v1][v2] = cost;
Cost_Matrix[v2][v1] = cost;
}
}
int Prim()
{
int k;
for(int i = 1; i <= N ;i++)
{
dist[i] = Cost_Matrix[1][i];
}
for(int i = 1; i < N; i++) /* 生成树还需要收n-1个节点 */
{
k = Find_Min();
if(!k) //k=0,表示找不到dist[V]最小者,则跳出
return -1;
else //k!=0说明找到dist[V]最小者
{
//将V收录进最小生成树,并累加器长度
TotalCost += dist[k];
dist[k] = 0;
//更新V的邻接点W的dist[W]值
for(int j = 2; j <= N; j++)
{
if(dist[j] && Cost_Matrix[k][j] < dist[j])
dist[j] = Cost_Matrix[k][j];
}
}
}
return TotalCost;
}
int Find_Min()
{
int k = 0;
int MinDist = INF;
for(int i = 2; i <= N ;i++)
{
if(dist[i] && dist[i] < MinDist)
{
MinDist = dist[i];
k = i;
}
}
return k;
}
Kruskal
#include <stdio.h>
#include <stdlib.h>
#define INF -1
typedef struct Noder{
int st; //源节点
int dst; //目的节点
int cost;//费用
}Noder;
Noder Edge[3001];
void GetInf(void);
void Init(void);
void MakeHeap(void);
Noder DeleteMin();
void Union(int Root1, int Root2);
int Find(int x);
int N, M;
int Father[1001];
int Size=0;
int main()
{
int TotalCost=0;
int k=1;
GetInf();
MakeHeap();
Init();
for(int i=0; i<M; i++)
{
Noder Temp=DeleteMin();
int x=Find(Temp.st);
int y=Find(Temp.dst);
if(x!=y)
{
TotalCost+=Temp.cost;
Union(Temp.st, Temp.dst);
k++;
if(k==N)
break;
}
}
if(k==N)
printf("%d",TotalCost);
else
printf("-1");
return 0;
}
//信息获取
void GetInf(void)
{
//获取城市数目和道路数目
scanf("%d %d", &N, &M);
//获取城市间道路
for(int i=1; i<=M; i++)
{
scanf("%d %d %d", &Edge[i].st, &Edge[i].dst, &Edge[i].cost);
}
}
//并查集数组初始化(对节点建立)
void Init(void)
{
//所有城市节点父节点初始化-1
for(int i=1; i<=N; i++)
{
Father[i]=-1;
}
}
//最小堆的建立(对边建立)
void MakeHeap(void)
{
int parent, child;
Size=M;
//哨兵
Edge[0].cost=INF;
for(int i=Size/2; i>=1; i--)
{
//从上往下进行过滤
Noder Temp=Edge[i];
for(parent=i; parent*2<=Size; parent=child)
{
child=parent*2;
//parent存在右子节点,child指向节点数据较小的
if(child!=Size && Edge[child].cost > Edge[child+1].cost)
{
child++;
}
//小数据子节点小于父节点,则子节点覆盖父节点
if(Edge[child].cost < Temp.cost)
{
Edge[parent]=Edge[child];
}
else
break;
}
Edge[parent]=Temp;
}
}
//从最小堆取最小元素,再调整成最小堆
Noder DeleteMin()
{
int parent;
int child;
//取出最小堆最小值
Noder Item=Edge[1];
//尾节点从上往下过滤
Noder Temp=Edge[Size--];
for(parent=1; parent*2<=Size; parent=child)
{
child=parent*2;
//parent存在右子节点,child指向节点数据较小的
if(child!=Size && Edge[child].cost > Edge[child+1].cost)
{
child++;
}
//小数据子节点小于父节点,则子节点覆盖父节点
if(Edge[child].cost < Temp.cost)
{
Edge[parent]=Edge[child];
}
else
break;
}
Edge[parent]=Temp;
return Item;
}
//集合的合并
void Union(int x, int y)
{
int root1=Find(x);
int root2=Find(y);
if(Father[root1]<Father[root2])//y的节点数目小于x的节点数目
{
Father[root1]+=Father[root2];
Father[root2]=root1;
}
else
{
Father[root2]+=Father[root1];
Father[root1]=root2;
}
}
//寻找元素所在的根节点的下标
int Find(int x)
{
if(Father[x]<=-1)
return x;
else
return Find(Father[x]);//Father[x]=
}
第二题代码
这里参考自https://www.cnblogs.com/8023spz/p/12264617.html
#include <stdio.h>
#include <stdlib.h>
//信息获取
void InfGet(void);
//信息处理
int InfProcess(void);
//节点数目、有向边数目
int N, M;
//入度为0的节点
int InNode[100];
//所有节点的入度数目
int InNum[100];
//邻接矩阵
int G[100][100];
//累积距离
int AccDis[100];
//队列数组下标
int head=0, tail=0;
//初始化累积距离和出队列节点数目
int Acc=0;
int main()
{
InfGet();
InfProcess();
if(tail>=N)
printf("%d", Acc);
else
printf("Impossible");
return 0;
}
//信息获取
void InfGet(void)
{
int a, b, c;
//获取节点数目与边数
scanf("%d %d", &N, &M);
//邻接矩阵初始化
for(int i=0; i<N; i++)
for(int j=0; j<N; j++)
G[i][j]=-1;
//初始化数组
for(int i=0;i<N;i++)
{
InNode[i]=0;
InNum[i]=0;
AccDis[i]=0;
}
//更新邻接矩阵 和 节点入度数目
for(int i=0; i<M; i++)
{
scanf("%d %d %d", &a, &b, &c);
G[a][b]=c;
InNum[b]++;
}
}
//信息处理
int InfProcess(void)
{
for(int i=0; i<N; i++)
{
if(!InNum[i])
InNode[tail++]=i;
}
while(head<=tail)
{
//V节点出队列
int V=InNode[head++];
//寻找目前最大的距离
if(AccDis[V]>Acc)
Acc=AccDis[V];
//V的邻接点W
for(int W=0;W<N;W++)
{
if(G[V][W]!=-1)
{
InNum[W]--;
if(!InNum[W])
InNode[tail++]=W;
//更新距离
if(AccDis[W]<AccDis[V]+G[V][W])
AccDis[W]=AccDis[V]+G[V][W];
}
}
}
}
第三题代码
这里是参考https://www.cnblogs.com/8023spz/p/12294805.html
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//信息获取
void InfGet(void);
//信息处理
void InfProcess(void);
//获取最早工期
int Getev(void);
//获取最晚工期
void Getlv(void);
//输出关键路径
void Solve(void);
//节点数目、有向边数目
int N, M;
//节点
int InOutNode[101];
//所有节点的入度数目
int InNum[101];
//所有节点的出度数目
int OutNum[101];
//邻接矩阵
int G[101][101];
//队列数组下标
int head = 0, tail = 0;
//初始化累积工期
int Acc = 0;
//最早结束工期
int Earliest[101];
//最晚开始工期
int Latest[101];
int main()
{
InfGet();
InfProcess();
return 0;
}
//信息获取
void InfGet(void)
{
int a, b, c;
scanf("%d %d", &N, &M);
memset(G, 0, sizeof(G));
for (int i = 1; i <= M; i++)
{
scanf("%d %d %d", &a, &b, &c);
G[a][b] = c;
InNum[b]++;
OutNum[a]++;
}
for (int i = 1; i <= N; i++)
{
Earliest[i] = 0;
Latest[i] = 0;
InOutNode[i] = 0;
}
}
//信息处理
void InfProcess(void)
{
if (!Getev())
printf("0");
else
{
printf("%d\n", Acc);
Getlv();
Solve();
}
}
//获取最早的工期
int Getev(void)
{
//队列头尾下标
head = 0;
tail = 0;
//获取入度为0的节点
for (int i = 1; i <= N; i++)
{
if (!InNum[i])
InOutNode[tail++] = i;
}
while (head<tail)
{
//出队
int V = InOutNode[head++];
//更新工期
if (Earliest[V]>Acc)
Acc = Earliest[V];
//邻接点入队
for (int W = 1; W <= N; W++)
{
if (G[V][W])
{
InNum[W]--;
if (!InNum[W])
InOutNode[tail++] = W;
if (Earliest[W]<Earliest[V] + G[V][W])
Earliest[W] = Earliest[V] + G[V][W];
}
}
}
return tail == N;
}
//获取最晚的工期
void Getlv(void)
{
//队列头尾下标
head = 0;
tail = 0;
//获取出度为0的节点
for (int i = 1; i <= N; i++)
{
if (!OutNum[i])
InOutNode[tail++] = i;
Latest[i] = Acc;
}
while (head<tail)
{
//出队
int V = InOutNode[head++];
for (int W = 1; W <= N; W++)
{
if (G[W][V])
{
OutNum[W]--;
if (!OutNum[W])
InOutNode[tail++] = W;
if (Latest[W]>Latest[V] - G[W][V])
Latest[W] = Latest[V] - G[W][V];
}
}
}
}
void Solve(void)
{
for (int i = 1; i <= N; i++)
{
//这个点可太妙了,降低了复杂度
if (Earliest[i] != Latest[i])
continue;
for (int j = N; j >= 1; j--)//逆序
{
if (G[i][j] && (Earliest[j] == Latest[j]) && (Latest[j] - Earliest[i] == G[i][j]))
printf("%d->%d\n", i, j);
}
}
}