浙江大学MOOC数据结构-陈越、何钦铭 编程练习题(第八讲)

本文详细解析了浙江大学MOOC数据结构课程中的三道编程题,包括Prim算法、Kruskal算法实现最小生成树,拓扑排序计算最长路径,以及关键路径法求解最早最晚工期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浙江大学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);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值