ACM暑期集训6

今天主要学习了最小生成树和二分图初步

1.最小生成树

1)prime算法

从某个起点s开始,不断地在未标记的点中,取与已标记的点(已加入生成树的点)所连的边权值最小的点,将这个点标记并加入最小生成树中。

重复直到所有的顶点都被加入到最小生成树中为止。

实现步骤:

vis[]标记节点,Map[][]存图

dis[i]表示节点i到已标记的点的集合的最短距离

(1)初始化:在起点处标记,dis[]设成到起点的距离

(2)循环n-1次,每次找到未标记的dis[]最小的节点,将其添加到生成树中并标记。然后对尚未标记的点尝试更新dis[]。

int prim(int s)
{
    int res=0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<n;i++) dis[i]=w[s][i];
    vis[s]=1;
    for(int i=1;i<n;i++)
    {
        int minn=INF,pos=0;
        for(int j=1;j<n;j++)
            if(!vis[j]&&minn>dis[j])
            minn=dis[j],pos=j;
        vis[pos]=1;
        res+=minn;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[pos][j])
                dis[j]=w[pos][j];
        }
    }
    return res;
}

2)kruskal算法

连通分支:可以看作是图中一整块独立且连通的点和边

将所有的边按照权值由小到大排序,依次遍历每一条边。如果边的两端点不属于同一集合,那么就将这两个点合并到同一集合,直到所有的点都属于同一集合。这时所有的点和合并了端点的边组成了这个最小生成树。(不断地合并能构成最小生成树的连通分支)

合并操作:并查集实现

int maxn;
int pre[105];
struct node
{
    int from;
    int to;
    int w;
}e[maxn];

bool cmp(node a,node b)
{
    return a.w<b.w;
}

int fin(int x)
{
    if(x==pre[x]) return x;
    else return pre[x]=fin(pre[x]);
}

int kruskal()
{
    int ans=0,cnt=0;
    sort(e,e+m,cmp);
    for(int i=0;i<m;i++)
    {
        int fx=fin(e[i].from);
        int fy=fin(e[i].to);
        if(fx!=fy)
        {
            pre[fy]=fx;
            ans+=e[i].w;
            cnt++;
        }
        if(cnt==n-1)
            return ans;
    }
    return -1;
}

2.二分图 

匹配:对于二分图G=<V,E>,   设边集M为E的子集,且M中   任意两条边都不存在公共顶   点,则称M是G的一个匹配。

最大匹配:所含边数最多的匹配

完全匹配:每个顶点都和匹配中的某条边相关联。

增广路径: 设M为二分图G已匹配边的集合,若P是图G中一条连通两个未匹配顶点的路径(P的起点终点在不同两侧)

并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。

1)最大匹配的匈牙利算法

基本思想:不断寻找增广路以增大匹配数

(1)初始时将匹配的边集M置为空

(2)找出一条增广路P,对P上的边取反获得更大的M’来代替M

(3)重复步骤(2)直至找不到新的增广路

int hungary()
{
     int ans=0;  //-1表示初始时没有点被匹配
     memset(link,-1,sizeof(link));
     for(int i=1;i<=nx;i++) //遍历X部的每一个点
     {
     if(link[i]==-1) //如果尚未被匹配
     {
     memset(vis,0,sizeof(vis));
     if(dfs(i)) ans++;  //尝试寻找增广路
     }
     }
     return ans;
}
bool dfs(int u)
{//遍历Y部的每一个点
for(int i=1;i<=ny;i++)
{//找一个和u有边相连并且还没有访问的点
     if(map[u][i]&&!vis[i])
     {
     vis[i]=1;
     if(link[i]==-1||dfs(link[i]))
     {//如果这个点尚未匹配
      //或者已经匹配且从匹配点出发能在寻找一条增广路
      link[u]=i;
      return ture;  //u和i匹配成功
     }
     }
}
return false;  //没有增广路
}

点覆盖:选取顶点集V的一个子集S,使得图中每条边都至少有一个端点在S中。

                          二分图最小点覆盖=最大匹配数

独立集:选取顶点集V的一个子集S,使得S中任意两个点之间都没有路径。

边覆盖:选取边集E的一个子集P,使得图中每一个顶点都是P中某条边的一个端点。            

                         二分图最大独立集=最小边覆盖=顶点总数目-最小点覆盖                    

                                                                        即顶点总数目-最大匹配数

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值