第三章 搜索与图论(三)(最小生成树,二分图)

一、最小生成树算法

稠密图使用prim算法,稀疏图使用kruskal算法

第三章 搜索与图论(三)(最小生成树,二分图)_第1张图片

第三章 搜索与图论(三)(最小生成树,二分图)_第2张图片 

   二、prim算法求最小生成树

prim和dijkstra算法类似,都是找到符合某种条件的点,然后更新。prim使用到已经构成的部分最小树所有结点中最小的距离。dijkstra算法是使用到起点最小的距离。

#include
//858 prim最小生成树 (稠密图做法)
using namespace std;
const int N=210,INF=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
   int res=0;
   for(int i=0;idist[j]))
              t=j;
       }
       //如果经过一个循环之后t找到的最短边仍然是INF,但是在第一次的时候所有的都是INF
       if(i&&dist[t]==INF)return INF;
       st[t]=true;
       //由于存在负权自环所以在更新与点相连的所有边之前就保存边
       if(i) res+=dist[t];
       //展示了与dijkstra算法的不同之处,dist保存的是边,而不是某种路
       for(int j=1;j<=n;j++)dist[j]=min(dist[j],g[t][j]);
   }
   return res;
}

int main()
{
    cin>>n>>m;
    memset(g,0x3f,sizeof g);
    memset(dist,0x3f, sizeof dist);

    for(int i=0;i>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    int ans=prim();
    if(ans==INF)puts("impossible");
    else cout<

三、kruskal算法

思路简单:

第三章 搜索与图论(三)(最小生成树,二分图)_第3张图片

#include
//858 prim最小生成树 (稠密图做法)
using namespace std;
const int N=210,INF=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int p[N];
struct Edge
{
    int a,b,w;
    bool operator <(const Edge &W)const
    {
        return w>n>>m;
    for(int i=0;i>a>>b>>c;
      edges[i]={a,b,c};
    }
    sort(edges,edges+m);
    for(int i=0;i<=n;i++)p[i]=i;
    int res=0,cnt=0;
    //遍历所有边
    for(int i=0;i

 四、二分图(染色法判断是否有奇数环)

第三章 搜索与图论(三)(最小生成树,二分图)_第4张图片

一个图是二分图当且仅当图中不含有奇数环(环中边的数量是奇数)

如果图当中不含有奇数环染色过程中没有矛盾。

思路:因为有多个连通块,而每次dfs就把连通块里面的所有的点全部遍历了,然后找其他连通块里面的点。

染色法dfs判断奇数环:对于当前点连接的所有边,判断这个点是否已经被染色,没有被染色继续染色,染色了,如果和相连的点的颜色相同,说明有奇数环。

#include
//858 prim最小生成树 (稠密图做法)
using namespace std;
const int N=100010,M=200010;
int m,n;
int color[N];//染色情况
int e[N],ne[N],h[N],idx;
void add(int a,int b)
{
    //e保存边idx的终点是什么
    //ne保存idx这条边连接的下一条边
    //更新h的值
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//对一个连通块进行深度优先遍历
bool dfs(int u,int c)
{
   color[u]=c;
   for(int i=h[u];i!=-1;i=ne[i])
   {
       int j=e[i];
       if(!color[j])
       {
           //只要有一个不通就返回false
           if(!dfs(j,3-c))return false;
       }
       else if(color[j]==c)return false;
   }
   return true;

}
int main()
{
    //cmemset(color,0,sizeof color);
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i>a>>b;
        add(a,b),add(b,a);
    }
    int flag=0;
    //遍历所有还没有遍历到的连通块,
    for(int i=1;i<=n;i++)
    {
     if(!color[i])
     {
      if(!dfs(i,1))//随便取一个颜色,因为相邻两次循环就
       {
         flag=1;
         break;
       }
     }

    }
    if(flag) puts("No");
    else puts("Yes");

}

五、匈牙利算法(两组结点最多的匹配)

首先按照第一选择进行匹配,后面匹配冲突的时候,去看先匹配的那个可不可以调整。

第三章 搜索与图论(三)(最小生成树,二分图)_第5张图片

使用bool数组st保证回去调整的时候不会选到同一个点,也就是一个点不会被同一个点匹配到两次。

#include
//861二分图的最大匹配(匈牙利算法)
using namespace std;
const int N=500,M=100010;
int e[N],ne[M],h[N],idx;
int st[N];
int match[N],n1,n2,m;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

int find(int u)
{

    for(int i=h[u];i!=-1;i=ne[i])//与u相连的所有点
    {
        int j=e[i];
        //因为可能在调整的过程中重复调用find函数,此时st[j]已经为true
        //不会再选这个点了
        if(!st[j])//之前有没有被考虑过
        {
            st[j]=true;
            //如果与其相连的点没有匹配
            //或者匹配了但是可以更换
            if(match[j]==0||find(match[j]))
            {
                //匹配上一个点
                match[j]=u;
                return true;
            }

        }
    }
    //尝试了所有的出边所连的点都不行的时候
    return false;
}
int main()
{
  cin>>n1>>n2>>m;
  while(m--)
  {
      int a,b;
      cin>>a>>b;
      add(a,b);
  }
  int res=0;
  for(int i=1;i<=n1;i++)
  {
      if(find(1))res++;
  }
  cout<

你可能感兴趣的:(蓝桥准备,图论,算法)