专题训练之图论

一、图论是什么

百度百科:图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。
链接:图论

在我的理解里,图论就是研究各种图,将他们存储起来,用算法解决图中的问题,如最短路等。

二、图论中的基本概念

图论中涉及许多概念,包括图的基本结构:节点和边,图的类型:有向图和无向图等等。我们一般用节点和边的集合来表示一个图。

图论的基本概念有很多,这里便不一一介绍,详细请看图论基本概念

三、图的储存

1.邻接矩阵

最简单粗暴的储存方式,可以vector数组或者直接用二维数组。map[i][j]表示i到j这条边的权值,如为0则为不通。
优点:容易理解,容易使用。
缺点:使用的时候时间复杂度高,空间复制度高

2.邻接链表

• 对于每个点vi开头指针h[i]=NULL,表示一条起始点为vi的边的位置。
• 例如:h[1]=2’->3’->4’->NULL,表示三条边,分别是1->2 ,1->3 ,1->4
• X’表示边的编号,即下面的cnt
• 对于边,用edge{ to ,next }的结构体存放,to表示这条边的结束点,next
表示下一条边。
• 那么对于加u->v边操作,edge[++cnt].to=v; .next=h[u]; h[u]=cnt;

3.链式前向星

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 100;
const int MAXM = 100;
 
int head[MAXN];
struct EdgeNode
{
    int to;
    int w;
    int next;
};
EdgeNode Edges[MAXM];
 
int main()
{
    int n,m,x,y,w;
    cin >> n >> m;
    memset(head,-1,sizeof(head));
 
    for(int i = 0; i < m; i++)
    {
        cin >> x >> y >> w;
        Edges[i].to = y;
        Edges[i].w = w;
        Edges[i].next = head[x];
        head[x] = i;
    }
 
    cout << endl;
    for(int i = 1; i <= n; i++)
    {
        for(int k = head[i]; k!= -1; k = Edges[k].next)
            cout << i << ' ' << Edges[k].to << ' ' << Edges[k].w << endl;
    }
 
    return 0;
}

我个人认为邻接链表应该是比较好的一种…

四、最短路径问题

1.Bellman-Ford算法(求单源最短路径)

用d[i]=min(d[j]+j到i的边的权值)不断地去递推更新d的值

2.Dijkstra算法(求单源最短路径)

个人理解为Ford的改进,但不能用为有负边的情况

3.Floyd算法(求各顶点之间最短路径)

用dp的思想,dp[i][j]记录i到j的最短路径
通过三层循环最终推出dp中的所有值
缺点:时间复杂度过高,为v的三次方(v为顶点个数)

五、并查集

板子题:【模板】并查集
可以用于最小生成树问题

#include <iostream>

using namespace std;

int father[10010];

void creat(int n)
{
  for (int i=1;i<=n;i++)
        father[i]=i;
}

int findd(int x)
{
    while (father[x]!=x)
        x=father[x]=father[father[x]];
    return x;
}
int main()
{
    int n,m;
    cin>>n>>m;
    creat(n);
    while (m--)
    {
        int z,x,y;
        cin>>z>>x>>y;
        int a=findd(x),b=findd(y);
        if (z==1)
        {
            father[a]=b;
        }else
        {
            if (a==b)
                cout<<"Y"<<endl;
            else cout<<"N"<<endl;
        }
    }
    return 0;
}

六、本专题题解

1.Cow Contest

题目链接

思路:因数据很小,可以利用Floyd去计算各个顶点直接是否相连,然后计算各个顶点分别与多少个顶点相连。

#include <iostream>

using namespace std;

int main()
{
    int n,m,i,j,k,ans=0;
    cin>>n>>m;
    int dp[110][110]= {};
    for (i=0; i<m; i++)
    {
        int a,b;
        cin>>a>>b;
        dp[a][b]=1;
    }
    for (k=1; k<=n; k++)
        for (i=1; i<=n; i++)
            for (j=1; j<=n; j++)
                if (dp[i][k]&&dp[k][j])
                    dp[i][j]=1;
    for (i=1; i<=n; i++)
    {
        int t=0;
        for (j=1; j<=n; j++)
            if (dp[i][j])
                t++;
        for (j=1; j<=n; j++)
            if (dp[j][i])
                t++; 
        if (t==n-1)
            ans++;
    }
    cout<<ans<<endl;
    return 0;
}

2.最短路

题目链接

思路:板子题,因为数据小,所以可以用Floyd求出。

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    while (1)
    {
        int n,m,i,k,j;
        cin>>n>>m;
        int f[110][110]={};
        memset(f,0x3f,sizeof(f));
        if (n==0&&m==0)
            break;
        for (i=0;i<m;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            f[a][b]=c;
            f[b][a]=c;
        }
        for (k=0;k<n;k++)
            for (i=1;i<=n;i++)
             for (j=1;j<=n;j++)
             f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
        cout<<f[1][n]<<endl;
    }
    return 0;
}

3.六度分离

题目链接

思路:用Floyd求出各个顶点之间的距离,看有没有超过7的

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    int n,m;
    while (cin>>n>>m)
    {
        int i,j,k;
        int dp[110][110]= {};
        memset(dp,0x3f,sizeof(dp));
        for (i=0; i<m; i++)
        {
            int a,b;
            cin>>a>>b;
            dp[a][b]=1;
            dp[b][a]=1;
        }
        for (k=0; k<n; k++)
            for (i=0; i<n; i++)
                for (j=0; j<n; j++)
                   // if (dp[i][k]&&dp[k][j])
                        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
        int p=1;
        for (i=0; i<n; i++)
        {
            for (j=i+1; j<n; j++)
                if (dp[i][j]>7)
                    p=0;
        }
        if (p)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值