图论的一些事儿 模版

本文探讨了图论中的经典问题,包括如何求解最小生成树,如Prim算法与Kruskal算法,并对比了它们的实现。同时介绍了地杰斯特拉算法用于寻找有向图中的一点到另一点的最短路径,强调权值必须为非负。最后提到了Floyd算法,用于计算有向图中任意两点间的最短路径。

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

最小生成树(求的就是给定一个无向图,挑选若干条边,连成一个树行图(无圈),使得所选边的权至和最小。)

Prim:

//prim算法

int prim(int n)
{
    int i,j,min,pos;
    int sum=0;
    memset(isvisited,false,sizeof(isvisited));
    //初始化
    for (i=1;i<=n;i++)
    {
        dist[i]=map[1][i];
    }
    //从1开始
    isvisited[1]=true;
    dist[1]=MAX;
    //找到权值最小点并记录下位置
    for (i=1;i<n;i++)
    {

        min=MAX;
        //pos=-1;
        for (j=1;j<=n;j++)
        {

            if (!isvisited[j] && dist[j]<min)
            {
                min=dist[j];
                pos=j;
            }
        }

        sum+=dist[pos];//加上权值
        isvisited[pos]=true;
        //更新权值
        for (j=1;j<=n;j++)
        {
            if (!isvisited[j] && dist[j]>map[pos][j])
            {
                dist[j]=map[pos][j];
            }
        }
    }
    return sum;

}


感悟: 最小生成树的代码与地杰斯特拉有着相像之处 ,prim算法是相加的是权重而地杰斯特拉增加的是下一个边

还有一种算法和prim的代码功能相同是kruskal,但是代码量大~

地杰斯特拉(求的是从一点到另一点的最短路径)(有向图)权值不能为负值

代码:

/***************************************
* About:    有向图的Dijkstra算法实现
***************************************/
 
#include <iostream>
using namespace std;
 
const int maxnum = 100;
const int maxint = 999999;
 
 
void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
{
    bool s[maxnum];    // 判断是否已存入该点到S集合中
    for(int i=1; i<=n; ++i)
    {
        dist[i] = c[v][i];
        s[i] = 0;     // 初始都未用过该点
        if(dist[i] == maxint)
            prev[i] = 0;
        else
            prev[i] = v;
    }
    dist[v] = 0;
    s[v] = 1;
 
    // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
    // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
    for(int i=2; i<=n; ++i)
    {
        int tmp = maxint;
        int u = v;
        // 找出当前未使用的点j的dist[j]最小值
        for(int j=1; j<=n; ++j)
            if((!s[j]) && dist[j]<tmp)
            {
                u = j;              // u保存当前邻接点中距离最小的点的号码
                tmp = dist[j];
            }
        s[u] = 1;    // 表示u点已存入S集合中
 
        // 更新dist
        for(int j=1; j<=n; ++j)
            if((!s[j]) && c[u][j]<maxint)
            {
                int newdist = dist[u] + c[u][j];  //松弛就是这里与prim算法不同……
                if(newdist < dist[j])
                {
                    dist[j] = newdist;
                    prev[j] = u;
                }
            }
    }
}
 
void searchPath(int *prev,int v, int u)
{
    int que[maxnum];
    int tot = 1;
    que[tot] = u;
    tot++;
    int tmp = prev[u];
    while(tmp != v)
    {
        que[tot] = tmp;
        tot++;
        tmp = prev[tmp];
    }
    que[tot] = v;
    for(int i=tot; i>=1; --i)
        if(i != 1)
            cout << que[i] << " -> ";
        else
            cout << que[i] << endl;
}
 
int main()
{
    freopen("input.txt", "r", stdin);
    // 各数组都从下标1开始
    int dist[maxnum];     // 表示当前点到源点的最短路径长度
    int prev[maxnum];     // 记录当前点的前一个结点
    int c[maxnum][maxnum];   // 记录图的两点间路径长度
    int n, line;             // 图的结点数和路径数
 
    // 输入结点数
    cin >> n;
    // 输入路径数
    cin >> line;
    int p, q, len;          // 输入p, q两点及其路径长度
 
    // 初始化c[][]为maxint
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=n; ++j)
            c[i][j] = maxint;
 
    for(int i=1; i<=line; ++i)  
    {
        cin >> p >> q >> len;
        if(len < c[p][q])       // 有重边
        {
            c[p][q] = len;      // p指向q
            c[q][p] = len;      // q指向p,这样表示无向图
        }
    }
 
    for(int i=1; i<=n; ++i)
        dist[i] = maxint;
    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=n; ++j)
            printf("%8d", c[i][j]);
        printf("\n");
    }
 
    Dijkstra(n, 1, dist, prev, c);
 
    // 最短路径长度
    cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;
 
    // 路径
    cout << "源点到最后一个顶点的路径为: ";
    searchPath(prev, 1, n);
}


数据:

5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60

floyd算法:(任意两点的最短路径)(有向图)

代码:

#include <cstdio>
 #include <algorithm>
 using namespace std;
 const int Ni=105;
 const int INF=10000000;
 
 int dist[Ni][Ni],g[Ni][Ni];
 int fa[Ni][Ni],path[Ni];
 
 int n,m,num,minc;
 
 void Floyd()
 {
     int i,j,k,p,tmp;
     minc=INF;
     for(k=1;k<=n;k++)
     {
         for(i=1;i<k;i++)
             for(j=i+1;j<k;j++)
             {
                 tmp=dist[i][j]+g[i][k]+g[k][j];
                 if(tmp<minc) //找到更优解
                 {
                     minc=tmp;
                     num=0;
                     p=j;
                     while(p!=i) //逆向寻找前驱结点直到找到最前面的i,i->…->j
                     {
                         path[num++]=p;
                         p=fa[i][p];//fa[i][j]保存的不是k,而是fa[k][j].
                     }
                     path[num++]=i;
                     path[num++]=k;
                 }
             }
         for(i=1;i<=n;i++)
             for(j=1;j<=n;j++)
             {
                 tmp=dist[i][k]+dist[k][j];
                 if(dist[i][j]>tmp)
                 {
                     dist[i][j]=tmp;
                     fa[i][j]=fa[k][j];
                 }
             }
     }
 }
 
 int main()
 {
     int i,j,u,v,w;
     while(~scanf("%d%d",&n,&m))
     {
         for(i=1;i<=n;i++)
             for(j=1;j<=n;j++)
             {
                 g[i][j]=INF;
                 dist[i][j]=INF;
                 fa[i][j]=i;
             }
         for(i=1;i<=m;i++)
         {
             scanf("%d%d%d",&u,&v,&w);
             w=min(g[u][v],w);          //处理重边
             g[u][v]=g[v][u]=dist[u][v]=dist[v][u]=w;
         }
         Floyd();
         if(minc==INF)
             printf("It's impossible.\n");
         else
         {
             printf("%d\n",minc);
             /*printf("%d",path[0]);
             for(i=1;i<num;i++)
                 printf(" %d",path[i]);
             printf("\n");*/
         }
     }
     return 0;
 }
from:http://acm.hdu.edu.cn/showproblem.php?pid=1599
bellman_fold算法:(含负权值有向图最优路径)
代码:
//x[i],y[i],w[i]分别是第 i条边的起点,终点和权值 
const int MAXN=99999;
bool bellman_ford()
{
    int i;
    for (i=0;i<n;i++)
        d[i]=MAXN;    //初始化,设d[i]无穷大 
    d[0]=0;           //第0个顶点处的距离为0 
    for (i=1;i<n;i++)
        for (j=1;j<=m;j++)
            if (d[x[j]]+w[j]<d[y[j]])  //枚举每一条边,并适当调整(取小权值) 
            {
                d[y[j]]=d[x[j]]+w[j];
                Prev[y[j]]=x[j];
            }
    for (i=1;i<=m;i++)
        if (d[x[j]]+w[j]<d[y[j]])
            return 0;

    return 1;
}

 

spfa算法:(bellman_ford的优化,可求含负值的任意两点的距离 单源最短路径 有向图无向图

代码:

SPFA算法模板1:(邻接矩阵)

const int nMax = 1000;           //  顶点的数量上限。
const int eMax = 2000;           //  边的数量上限。
const int inf = 0xffffff;
 
int n, edge[nMax][nMax], dict[nMax];
int queue[eMax];                 //  创建新队列,也可以用STL中的#include<queue>,但效率较低。
bool vis[nMax];
 
void init_data(s){
    for(i = 1; i <= n; i ++)     //  初始化dict[]。
        dict[i] = inf;
    dict[s] = 0;                 //  起点s。
}
 
void spfa(int s){                //  初始结点s,即为起点,若目标结点t,则输出dict[t]。
    init_data(s);
    int head = 0, tail = 1;
    // int path[Max];            //  可增加一个path[]数组来记录最后s到t的路径。
    queue[0] = x;
    dict[s] = 0;
    while(tail > head){
        int u = queue[head];
        vis[u] = true;
        for(i = 1; i <= n; i ++){
            if(dict[i] > dict[u] + edge[u][i]){
                dict[i] = dict[u] + edge[u][i];
                // path[i] = u;
                if(!vis[i]){      //  对以前没有访问过的顶点,加入队列中。
                    vis[i] = true;
                    queue[tail] = i;
                    tail ++;
                }
            }
        }
        vis[u] = false;           //  记得把出队列的顶点的vis[]置为false。
        head ++;
    }
}
 
 
SPFA算法模板2:(邻接表)
 
#include<iostream>
using namespace std;
 
#define MAXE 10000
#define MAXV 1000
int inf = 1000000000;

struct edge_t
{
    int s, t;
    int w;
    int next;
};
edge_t arrEdge[MAXE]; //arrEdge[1...szEdge]保存szEdge条边.
int szEdge;
int arrHead[MAXV];
int nv;
int arrPre[MAXV];
int arrDis[MAXV];
int Que[MAXE*10]; //队列
char inQue[MAXV]; //点v是否在队列中
 
void SPFA(int s)
{
    int i, ie, v, head, tail;
    edge_t e;
    for(i = 1; i <= nv; i++)
        arrDis[i] = inf, arrPre[i] = i;
    arrDis[s] = 0;
    arrPre[s] = -1;
    //初始化队列
    memset(Que, 0, sizeof(Que));
    memset(inQue, 0, sizeof(inQue));
    head = 0, tail = 1;
    Que[head] = s;
    inQue[s] = 1;
    while(head < tail)
    {
        v = Que[head++];
        inQue[v] = 0;
        for(ie = arrHead[v]; ie!=-1; ie = arrEdge[ie].next)
        {
            e = arrEdge[ie];
            if(arrDis[e.t] > arrDis[v] + e.w)
            {
                arrDis[e.t] = arrDis[v] + e.w, arrPre[e.t] = v;
                if(!inQue[e.t])
                {
                    inQue[e.t] = 1;
                    Que[tail++] = e.t;
                }
            }
        }
    }
    return ;
}
 
int main()
{
    int ne, i, s, t, w, p;
    memset(arrHead, -1, sizeof(arrHead));
    memset(arrEdge, 0, sizeof(arrEdge));
    szEdge = 0;
    scanf("%d %d", &nv, &ne);
    for(i = 1; i <= ne; i++)
    {
        scanf("%d %d %d", &s, &t, &w);
        szEdge++;
        arrEdge[szEdge].s = s;
        arrEdge[szEdge].t = t;
        arrEdge[szEdge].w = w;
        arrEdge[szEdge].next = arrHead[s];
        arrHead[s] = szEdge;
        szEdge++;
        arrEdge[szEdge].s = t;
        arrEdge[szEdge].t = s;
        arrEdge[szEdge].w = w;
        arrEdge[szEdge].next = arrHead[t];
        arrHead[t] = szEdge;
    }
    SPFA(1);
    for(i = 1; i <= nv; i++)
    {
        printf("Distance from 1 to %d is %d.\nPath: %d", i, arrDis[i], i);
        p = arrPre[i];
        while(p != -1)
        {
            printf(" <-- %d", p);
            p = arrPre[p];
        }
        printf("\n\n");
    }
    return 0;
} 


 

 



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值