tulun

图论

1.建图

1.邻接矩阵

(例:map[i][j]——————表示编号为i的点与编号为j的点有边。)

2.邻接表 (未学);

3.边集数组 (最常用)

代码

struct node
{
    int a,b,nx,c;
}t[100010];
int head[100010],n,nz;

void Build edge()
{
    int a,b,c;
    for(int i=1;i<=n;i++)
    {
        t[++nz].a=a;
        t[nz].b=b;
        t[nz].c=c;
        t[nz].nx=head[a];
        head[a]=nz;
    }
}

2.最短路径算法

1.单源最短路径

1.Dijkstra

代码

1.邻接矩阵

void dijkstra(int s)
{
    int i,j,Min,p;
    for(i=1;i<=n;i++)d[i]=inf;
    d[s]=0;v[s]=true;
    for(j=1;j<=n;j++)
    {
        Min=inf;
        for(i=1;i<=n;i++)
            if((!v[i])&&(d[i]<Min)){Min=d[i];p=i;}
        v[p]=true;
        for(i=1;i<=n;i++)
            if(a[p][i]!=0&&d[i]>d[p]+a[p][i])
                d[i]=a[p][i];
    }
}

2.边集数组

void dijkstra(int s)
{
    int i,j,k,Min,v;
    memset(vis,false sizeof(vis));
    for(i=0;i<=n;i++)h[i]=inf;
    h[s]=0;
    for(i=1;i<=n;i++)
    {
        Min=inf;k=-1;
        for(j=1;j<=n;j++)
            if((vis[j]==false)&&(Min>h[j]))
            {
                Min=h[j];
                k=j;
            }
        if(k==-1)break;
        vis[k]=true;
        j=head[k];
        while(j!=-1)
        {
            v=t[i].v;
            if(h[v]>h[k]+t[i].c)h[v]=h[k]+t[i].c;
            j=t[i].nx;
        }
    } 
}

2.spfa(可判断负环)

int spfa(int src,int n)
{
    int i;
    for(i=1;i<=n;i++)
    {
        vis[i]=0;
        dis[i]=inf;
    }
    dis[src]=0;
    int q[110],l=1,r=1;
    q[src]=src;
    vis[src]=true;
    while(l<=r)
    {
        int u,v;
        u=Q[l];
        vis[u]=false;
        for(i=head[u];i>=0;i=t[i].nx)
        {
            v=t[i].y;
            if(dis[u]+t[i].c<dis[v])
            {
                dis[v]=dis[u]+t[i].c;
                if(if(!vis[v]))
                {
                    r++;
                    q[r]=v;
                    vis[v]=true;
                }
            }
        }
        r++;
    }
    return dis[n];
}

2.多源最短路径
1.floyd

void floyd()
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(it j=1;j<=n;j++)
                if(f[i][j]>f[i][k]+f[k][j])
                    f[i][j]=f[i][k]+f[k][j]
}

3.图的联通性问题

1.无向图的最小环问题(floyd)

代码

#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,g[1001][1001],dis[1001][1001];
int floyd()
{
    int Mnin=999999999ll;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<k;i++)
            for(int j=i+1;j<k;j++)
                Mnin=min(Mnin,g[i][k]+g[j][k]+dis[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
                dis[j][i]=dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    }
    return Mnin;
}
int main()
{
    cin>>n>>m;
    int a,b,c;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(i==j)g[i][j]=dis[i][j]=0;
    else g[i][j]=dis[i][j]=999999999ll;
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=dis[a][b]=dis[b][a]=c;
    }

    int v=floyd();
    if(v==999999999ll)cout<<"impossible"<<endl;
    else cout<<v<<endl;
    return 0;
}

2.欧拉回路

1.判断

1.对于无向图:

  存在欧拉回路的条件:每个点的度都为偶数;

  存在欧拉路的条件:有且只有两个点的度为一,且这两个点分别为起点和终点

2.对于有向图:

  存在欧拉回路的条件:每个点出度等于入度;

  存在欧拉路的条件:存在一个点出度比入度多一作为起点,存在一点入度比出度多一作为终点,其余点出度等于入度;

2.dfs搜索;

在搜素的过程中,记录搜索次序;

3.(Fleury)佛罗莱算法

见博文(Fleury)佛罗莱算法;

4.并查集

代码

int find(int x)
{
    if(f[x]==x)return f[x];
    return f[x]=find(f[x]);
}
void together(int a,int b)
{
    int aa=find(a),bb=find(b);
    f[aa]=f[bb];
}

5.最小生成树

1.prim算法
说明

建立一棵树,每次加上这棵树的最近相邻接点,然后更新与新的树相邻的节点的距离,直到顶点都加入了树(可用优先队列优化);

代码

void prim()
{
    memset(l,0x7ffff,sizeof(l));
    memset(vis,0,sizeof(vis));
    l[1]=0;
    for(int i=1;i<=n;i++)
    {
        int k=1;
        for(int j=1;j<=n;j++)
        if(!vis[j]&&l[k]>l[j])k=j;
        vis[k]=true;
        for(j=1;j<=n;j++)
            if(g[k][j]<l[j])l[j]=g[k][j];//g[k][j]---邻接矩阵;
    }
    int ans=0;
    for(int i=1;i<=n;i++)ans+=l[i];
    cout<<ans<<endl; 
}

kruskal

1.把图中的边权值按从小到大的顺序排序;

2.加上最短的边(边的两节点未连接,否则考虑下一条边【用并查集判断】);

3.一共添加(n-1)条边;

代码

int find(int x)
{
    if(f[x]==x)return f[x];
    return f[x]=find(f[x]);
}
void kruskal()
{
    sort(t+1,t+n+1);
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=n;i++)
        while(l<=nz)
        {
            l++;p=find(t[l].x);q=find(t[l].y);
            if(p!=q)
            {
                Min=Min+t[i].w;
                f[p]=q;
                break;
            }
        }
}

6.拓扑排序

将入度为0的点出栈;

代码

struct node
{
    int y,nx,w; 
}t[1000010];
int q[110110],d[10010],head[10010];
void top()
{
    cin>>n>>p;
    int a,b,c,nz=0;
    for(int i=1;i<=p;i++)
    {
        cin>>a>>b>>c;
        t[++nz].y=b;t[nz].w=c;t[nz].nx=head[a];head[a]=nz;
        d[b]++;
    }
    int l=1,r=0;
    for(int i=1;i<=n;i++)
    if(d[i]==0){r++;q[r]=i;}
    while(l<=r)
    {
        int u=q[l];
        for(int i=head[u];i;i=t[i].nx)
        {
            int v=t[v].y;
            d[v]--;
            if(d[i]==0)
            {
                r++;q[r]=v;
            }
        }
        l++;
    }
}

7.二分图(此处浅谈)

1.二分图的染色

核心思想:将一个图按照一定原则,进行黑白染色,若冲突,则不是二分图;

原则例子1:(双栈排序)若a[i],a[j],a[k]满足i<j<k,且a[i]>a[k]而且a[i]<a[j],则a[j]进入第二个栈;

2.二分图的最大匹配(匈牙利算法);

代码

bool dfs(int p)
{
    for(int i=1;1<=n;i++)
    {
        if(Map[p][i]&&(!chk[i]))
        {
            chk[i]=true;
            if((match[i]==0)||dfs(match[i]))
            {
                match[i]=p;return true;
            }
        }
    }
    return false;
}
void Hungary(int n)
{
    int ans=0;
    for(int i=1;i=n;i++)
    {
        memset(chk,0,sizeof(chk));
        if(dfs(i))ans++;
    }
    cout<<ans<<endl;
}
posted @ 2020-06-30 09:10  清晨的第一缕阳光  阅读(213)  评论(0编辑  收藏  举报