四、匈牙利算法,dijkstra,拓扑排序(附例题)

一、匈牙利算法模板

重点

  1. 匈牙利算法的时间复杂度为O(ve),其中v为二分图左边的顶点数,e为二分图中边的数目。

  1. 时间复杂度:邻接矩阵最坏为 O(n^3) 邻接表:O(mn)

空间复杂度:邻接矩阵:O(n^2) 邻接表:O(n+m)

  1. 二分图概念: 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(V1,V2),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集,则称图G为一个二分图。

  1. 最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配

  1. 完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配

6.相关定理

定理1:最大匹配数 = 最小点覆盖数

定理2:最大匹配数 = 最大独立数

定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

A.二分图最大匹配

Description

给定一个二分图,其左部点的个数为 n,右部点的个数为 m,边数为 e,求其最大匹配的边数。

左部点从 11 至 n 编号,右部点从 11 至 m 编号。

Input

输入的第一行是三个整数,分别代表 nme

接下来 e 行,每行两个整数 u,v,表示存在一条连接左部点 u 和右部点 v 的边。

Output

输出一行一个整数,代表二分图最大匹配的边数。

Sample 1

Inputcopy

Outputcopy

1 1 1

1 1

1

Sample 2

Inputcopy

Outputcopy

4 2 7

3 1

1 2

3 2

1 1

4 2

4 1

1 1

2

Hint

数据规模与约定

对于全部的测试点,保证:

  • 1≤n,m≤500。

  • 1≤e≤5×104。

  • 1≤un,1≤vm

不保证给出的图没有重边

代码:

#include<iostream>
#include<algorithm>
#include<string.h> 
using namespace std;
const int N=510;
int map[N][N];//存图 
int l[N];//右侧元素所对应的左侧元素 
bool vis[N];//标记是否被访问过 
int n,m,e,a,b;
bool math(int i)
{
    for(int j=1;j<=m;j++)
    {
        if(map[i][j]&&!vis[j])//如果关系存在,并且未被访问过 
        {
            vis[j]=true;
            if(l[j]==0||math(l[j]))//如过暂无匹配,或者原来匹配的左侧元素可以找到新元素 
            {
                l[j]=i;//让当前左侧元素成为右侧元素的新匹配 
                return true;//匹配成功 
             } 
        }
    }
    return false;//匹配失败 
}
int main()
{
    cin>>n>>m>>e;
    memset(map,0,sizeof(map));
    memset(l,0,sizeof(l));
    while(e--)
    {
        cin>>a>>b;
        map[a][b]=1;
    }
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,false,sizeof(vis));
        if(math(i))
            cnt++;
    }
    cout<<cnt<<endl; 
    return 0; 
}

二、迪克斯特拉算法模板(dijkstra)

重点:

1.时间复杂度

没有优化的dijkstra算法的时间复杂度为 O(n^2);

堆优化过的dijkstra算法时间复杂度为O(mlogn);(n表示点数,m表示边数)

2空间复杂度O(n^2),n表示节点个数

3.只适用于有向无环图如果有负权边,不能使用dijkstra算法

4.dijkstra计算加权图中的最短路径

B.Til the Cows Come Home

史学长很热爱学习,他打算假期偷偷跑回学校学习,为了多学习他希望可以找最快的路线回到学校。 洛阳市里有N个(2 <= N <= 1000)个地铁站,编号分别为1..N。他的家在1号地铁站旁边,洛阳师范学院站是N号地铁站。地铁站之间共有M (1 <= M <= 2000)条双向路径。 史学长现在在1号地铁站,他希望知道到学校最短要多长时间。可以保证史学长能到达学校。忽略史学长在换乘地铁时需要的等待时间。

Input

* 第一行输入两个整数m和n

* 接下来m行,每行三个整数a、b、c,表示a号地铁站和b号地铁站间要花费时间c(1<=c<=100).

Sample

Inputcopy

Outputcopy

5 5

1 2 20

2 3 30

3 4 20

4 5 20

1 5 100

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N=1010;
int p[N][N];//存图 
int dis[N];//代表从1到某个点的距离 
bool vis[N];//代表访问 
int n,m,a,b,c;
int dijkstra()
{
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;//初始化,相当于1到它本身的距离 
    for(int i=0;i<n-1;i++)//遍历除了1剩下的点 
    {
        int t=-1;//代表中转站
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&(t==-1||dis[t]>dis[j]))
            {
                t=j;//找到1到某个点的最短距离,并更新 
            }
        } 
        vis[t]=true;//标记,防止再次被访问
        for(int j=1;j<=n;j++)
        {
            dis[j]=min(dis[j],dis[t]+p[t][j]);//从直达和曲达找最短 
        } 
    }
    return dis[n];
}
int main()
{
    cin>>m>>n;//先输入m(多少条路),再输出n 
    memset(p,0x3f,sizeof(p));
    //for(int i=1;i<=n;i++)
        //p[i][i]=0; 
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        p[a][b]=p[b][a]=min(p[a][b],c);//双向路径 
    }
    cout<<dijkstra()<<endl;
    return 0;
}

三、拓扑排序

重点

1.对于n个定点,e条弧的有向图而言,时间复杂度:O(n+e)

2.拓扑排序就是在一个有向无环图(也称DAG图),将所有的点排成一个线性的序列,使得每条有向边的起点u都排在终点v的前面。

3在拓扑排序时,判环。所有点都入队说明存在拓扑序列,否则不存在,每次从队列中拿出一个数时,t加1,while循环结束的时候判断t是否等于节点总数,若不等于,则说明图是有环的。

4.如果在同等条件下,想要让编号小的顶点在前面,需要使用优先队列

C.

给出一个图的结构,输出其拓扑排序序列,要求在同等条件下,编号小的顶点在前。

Input

若干行整数,第一行有2个数,分别为顶点数v和弧数a,接下来有a行,每一行有2个数,分别是该条弧所关联的两个顶点编号。

v<=100, a<=500

Output

若干个空格隔开的顶点构成的序列(用小写字母)。

Sample

Inputcopy

Outputcopy

6 8

1 2

1 3

1 4

3 2

3 5

4 5

6 4

6 5

代码:

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=110;
int du[N];//入度
int n,m,a,b;
vector<int>v[N];
vector<int>ans;
priority_queue<int ,vector<int>, greater<int> >q;//优先队列 
void tuopusort()
{
    while(!q.empty())
    {
        int t=q.top();//取出栈顶元素
        ans.push_back(t);
        q.pop();//弹出
        for(int i=0;i<v[t].size();i++)
        {
            du[v[t][i]]--;//把这个点相关的边删除 
            if(du[v[t][i]]==0)//在找到下一个入度为0的点 
                q.push(v[t][i]);    
        } 
    }
}
int main()
{
    int i;
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
        cin>>a>>b;
        v[a].push_back(b);
        du[b]++;
    }
    for(i=1;i<=n;i++)
    {
        if(du[i]==0)
            q.push(i);
    }
    tuopusort();
    for(i=0;i<ans.size();i++)
    {
        cout<<"v"<<ans[i]<<" ";
    }
    cout<<endl;
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值