数据结构题目-图论算法

仅作储存代码用。

目录

问题 A: 图的最小生成树-Prim算法

问题 B: 图的最小生成树-Kruskal算法 

 问题 C: 算法7-9:最小生成树

 问题 D: 算法7-15:迪杰斯特拉最短路径算法

问题 E: 算法7-16:弗洛伊德最短路径算法 

 问题 F: 图的最短路径-Floyd算法输出最短路径包含的边


问题 A: 图的最小生成树-Prim算法

题目描述

Prim算法是求解带权图的最小生成树的经典算法。其步骤如下:
E1:任取一个顶点构成U={v0};构造向量cost[0…n-1]和adj[0…n-1],cost[i]表示顶点vi到U的最短边的长度,adj[i]表示顶点vi到U的最短边在U中的邻接点的下标;其中,vi∈V-U。初始时,生成树T为空集。
E2:重复n-1次
E21:从V-U中选出cost值最小的顶点vk,将边<vk, vadj[k]>加入到生成树T中,然后将vk并入U中;
E22:修正V-U中各顶点的cost值和adj值;

本题要求根据Prim算法,求解第一步状态下的cost和adj两个向量。

输入格式

输入为邻接矩阵存储的图,第一行为正整数n(小于100),表示图中顶点个数
接下来是n行,每行为n个空格隔开的非负整数。0表示两个顶点之间没有直达边,非0表示有直达边。且该数字为对应直达边的权重。

输出格式

假设第一步选择将序号最小的0号节点并入集合U。按照样例格式输出cost和adj两个向量

输入样例 复制

7
0 10 9 13 0 0 0
10 0 0 15 7 0 12
9 0 0 4 0 3 0
13 15 4 0 0 22 23
0 7 0 0 0 0 20
0 0 3 22 0 0 32
0 12 0 23 20 32 0

输出样例 复制

0 - -
1 10 0
2 9 0
3 13 0
4 - -
5 - -
6 - -

数据范围与提示

样例对应图示如下:
 

#include<bits/stdc++.h>
using namespace std;
int a[105][105];
int n;


int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>a[i][j];
        }
    }
    for(int j=0;j<n;j++)
    {
        cout<<j<<" ";
        if(a[0][j]!=0)
        {
            cout<<a[0][j]<<" "<<0<<endl;
        }
        else
        cout<<"- - "<<endl;;
    }
}

问题 B: 图的最小生成树-Kruskal算法 

题目描述

Kruskal算法是最小生成树的经典算法,其步骤为:
E1:将所有的边按权值排序;
E2:设每个顶点为一个独立的点集,生成树T为空集;
E3:依序扫描每一条边<vi,vj>,直到已输出n-1条边:
E31:若vi、vj不在同一点集中,则将该边加入生成树T中,并合并这两个点集;否则舍弃该边;
本题要求读入带权图,对其所有边按权值排序后输出。

输入格式

输入为邻接矩阵存储的图,第一行为正整数n(小于100),表示图中顶点个数
接下来是n行,每行为n个空格隔开的非负整数。0表示两个顶点之间没有直达边,非0表示有直达边。且该数字为对应直达边的权重。

输出格式

对所有边按权重排序后输出。如果图只有1个点(即没有边),则直接输出空行。

输入样例 复制

7
0 10 9 13 0 0 0
10 0 0 15 7 0 12
9 0 0 4 0 3 0
13 15 4 0 0 22 23
0 7 0 0 0 0 20
0 0 3 22 0 0 32
0 12 0 23 20 32 0

输出样例 复制

<2,5>:3
<5,2>:3
<3,2>:4
<2,3>:4
<4,1>:7
<1,4>:7
<2,0>:9
<0,2>:9
<1,0>:10
<0,1>:10
<1,6>:12
<6,1>:12
<0,3>:13
<3,0>:13
<1,3>:15
<3,1>:15
<4,6>:20
<6,4>:20
<3,5>:22
<5,3>:22
<6,3>:23
<3,6>:23
<5,6>:32
<6,5>:32

数据范围与提示

注意题目的测试数据构造时用的排序算法如下:
for(int i=0;i<n;i++)
   for(int j=i+1;j<n;j++)
        if(a[j]<a[i]) swap(i,j);  // 交换

#include<bits/stdc++.h>
using namespace std;
struct info{
    int x;
    int y;
    int num;
}a[10005];


bool mysort(struct info b,struct info c)
{
    return b.num<c.num;
}

int main()
{
    int n;
    cin>>n;
    int index=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            int temp;
            cin>>temp;
            if(temp!=0)
            {
                a[index].x=i;
                a[index].y=j;
                a[index].num=temp;
                index++;
            }
        }
    }
    for(int i=0;i<index;i++)
    {
        for(int j=i+1;j<index;j++)
        {
            if(a[j].num<a[i].num)
            {
                int tempx=a[i].x,tempy=a[i].y,tempnum=a[i].num;
                a[i].x=a[j].x;
                a[i].y=a[j].y;
                a[i].num=a[j].num;
                a[j].x=tempx;
                a[j].y=tempy;
                a[j].num=tempnum;
            }
        }
    }
    for(int i=0;i<index;i++)
    {
        cout<<"<"<<a[i].x<<","<<a[i].y<<">"<<":"<<a[i].num<<endl;
    }
}

 问题 C: 算法7-9:最小生成树

题目描述

最小生成树问题是实际生产生活中十分重要的一类问题。假设需要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路。这时,自然需要考虑这样一个问题,即如何在最节省经费的前提下建立这个通信网。

可以用连通网来表示n个城市以及n个城市之间可能设置的通信线路,其中网的顶点表示城市,边表示两个城市之间的线路,赋于边的权值表示相应的代价。对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。现在,需要选择一棵生成树,使总的耗费最小。这个问题就是构造连通网的最小代价生成树,简称最小生成树。一棵生成树的代价就是树上各边的代价之和。

而在常用的最小生成树构造算法中,普里姆(Prim)算法是一种非常常用的算法。以下是其算法的大致结构:

在本题中,读入一个无向图的邻接矩阵(即数组表示),建立无向图并按照以上描述中的算法建立最小生成树,并输出最小生成树的代价。

输入格式

输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。 以后的n行中每行有n个用空格隔开的整数,对于第i行的第j个整数,如果不为0,则表示第i个顶点和第j个顶点有直接连接且代价为相应的值,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。 输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图,且保证图中只有一个连通分量。

输出格式

只有一个整数,即最小生成树的总代价。请注意行尾输出换行。

输入样例 复制

4
0 2 4 0
2 0 3 5
4 3 0 1
0 5 1 0

输出样例 复制

6

数据范围与提示

*** 提示已隐藏,点击此处可显示 ***

收起提示[-]

在本题中,需要掌握图的深度优先遍历的方法,并需要掌握无向图的连通性问题的本质。通过求出无向图的连通分量和对应的生成树,应该能够对图的连通性建立更加直观和清晰的概念。

#include<bits/stdc++.h>
using namespace std;
int a[55][55];
int n;
int dis[55];
int ans;
bool book[55];


void prime()
{
    dis[0]=0;
    book[0]=true;
    for(int i=1;i<n;i++)
    {
        if(a[0][i]!=0)
        dis[i]=a[0][i];
    }
    for(int i=1;i<n;i++)
    {
        int temp=99999;
        int index;
        for(int j=1;j<n;j++)
        {
            if(!book[j]&&dis[j]<temp)
            {
                temp=dis[j];
                index=j;
            }
        }
        book[index]=true;
        ans+=dis[index];
        for(int j=1;j<n;j++)
        dis[j]=min(dis[j],a[index][j]);
    }
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        dis[i]=99999;
        for(int j=0;j<n;j++)
        {
           cin>>a[i][j];
        }
    }
    prime();
    cout<<ans<<endl;
    return 0;
}

 问题 D: 算法7-15:迪杰斯特拉最短路径算法

在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。

在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。

可将迪杰斯特拉算法描述如下:


设辅助向量u[0…n-1]、shortest[0…n-1]和path[0…n-1];u[i]为1表示从v0到vi的最短路径已经求出,为0表示尚未求出;shortest[i]记录目前已知的从v0到vi的较短路径的长度;path[i]记录目前已知的从v0到vi的较短路径;
初始时,设置从v0到vi的直达弧为目前已知的较短路径;

E1:初始化辅助向量u、shortest、path;
E2:循环n-1次:
E21:从M-U中选择最小的shortest[k];
E22:将vk并入U中;
E23:对M-U中的各顶点vi的已知较短路径进行修正:若P0,k+{i}的长度短于目前已知的P0,i的长度,则用P0,k+{i}取代原P0,i;

在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出源点至每一个其它顶点的最短路径长度。 

输入格式

输入的第一行包含2个正整数n和s,表示图中共有n个顶点,且源点为s。其中n不超过50,s小于n。 以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。

输出格式

只有一行,共有n-1个整数,表示源点至其它每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。 请注意行尾输出换行。

输入样例 复制

4 1
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0

输出样例 复制

6 4 7 

数据范围与提示

*** 提示已隐藏,点击此处可显示 ***

收起提示[-]

在本题中,需要按照题目描述中的算法完成迪杰斯特拉算法,并在计算最短路径的过程中将每个顶点是否可达记录下来,直到求出每个可达顶点的最短路径之后,算法才能够结束。 迪杰斯特拉算法的特点是按照路径长度递增的顺序,依次添加下一条长度最短的边,从而不断构造出相应顶点的最短路径。 另外需要注意的是,在本题中为了更方便的表示顶点间的不可达状态,可以使用一个十分大的值作为标记。

#include<bits/stdc++.h>
using namespace std;

int dis[55][55];
bool visit[55]={false};

int main()
{
    int n,s;
    cin>>n>>s;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>dis[i][j];
            if(i!=j && dis[i][j]==0)
            dis[i][j]=99999;
        }
    }
    for(int k=0;k<n;k++)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(dis[i][k]+dis[k][j]<dis[i][j])
                dis[i][j]=dis[i][k]+dis[k][j];
            }
        }
    }
    for(int i=0;i<n;i++)
    {
        if(i!=s)
        {
            if(dis[s][i]!=99999)
            cout<<dis[s][i]<<" ";
            else
            cout<<"-1"<<" ";
        }
    }
    return 0;
}

问题 E: 算法7-16:弗洛伊德最短路径算法 

#include<bits/stdc++.h>
using namespace std;

int dis[55][55];
bool visit[55]={false};

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>dis[i][j];
            if(i!=j && dis[i][j]==0)
            dis[i][j]=99999;
        }
    }
    for(int k=0;k<n;k++)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(dis[i][k]+dis[k][j]<dis[i][j])
                dis[i][j]=dis[i][k]+dis[k][j];
            }
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(dis[i][j]!=99999)
            cout<<dis[i][j]<<" ";
            else
            cout<<"-1"<<" ";
        }
        cout<<endl;
    }
    return 0;
}

 问题 F: 图的最短路径-Floyd算法输出最短路径包含的边

题目描述

在Floyd算法求解图的最短路径过程中,会记录两个顶点之间最短路径所经过的边。
如下图例子所示,本题给出Floyd算法的最后结果,请求出任意两点之间最短路径长度,以及相应的最短路径所包含的边。
 

输入格式

输入第一行为正整数n,表示图的节点数量(小于100)
接下来是n*n的矩阵,每个元素包含两个整数,分别是两点之间最短路径长度,和路径经过的中间节点。
最后是两个整数p和q,表示两个顶点序号。

输出格式

第一行输出从顶点p到顶点q的最短路径长度。
第二行输出该最短路径经过的边对应的顶点序列。

输入样例 复制

6
-1 -1 9 4 18 4 12 0 5 0 13 1
6 1 -1 -1 24 4 18 0 11 0 4 1
10 2 19 4 -1 -1 14 2 15 0 18 3
13 5 11 3 31 4 -1 -1 18 0 4 3
10 1 4 4 13 4 22 0 -1 -1 8 1
9 5 18 4 27 4 21 0 14 0 -1 -1
4 3

输出样例 复制

22
4 1 0 3 

写不来,抄别人的。

#include<bits/stdc++.h>
using namespace std;

int dis[55][105];
bool visit[55]={false};

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<2*n;j++)
        {
            cin>>dis[i][j];
        }
    }
    int p,q;
    cin>>p>>q;
    cout<<dis[p][q*2]<<endl;
    stack<int>path;
    path.push(q);
    for(int i=p,j=q;dis[i][j*2+1]!=p;j=dis[i][j*2+1])
    path.push(dis[p][j*2+1]);
    path.push(p);
    while(!path.empty())
    {
        cout<<path.top()<<" ";
        path.pop();
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗯嗯你说的对

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值