• 有向图最小路径覆盖方法浅析、证明 //hdu 3861


    路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联。

    对于一个有向无环图怎么求最小路径覆盖?

    先构造二分图: 对于原图,先拆点,吧每个点i拆成ii,iii。若有边i--》j,则在二分图中,添加边 ii--》jjj(即原来每个点拆为一个入点和出点),这样构成二分图。

    则:最小路径覆盖数=原图顶点数-二分图最大匹配数。

    粗略解析证明:(设有n个顶点)

    若原图没有边,则最大匹配数为0,最小路径覆盖为n,思想:每得到一个匹配,相当于把这俩个点并为一个集合(原来有N个集合),即这俩个点在原图中是在同一条路径覆盖上的,每次成功匹配,相当于一次成功“并集”,所谓的路径覆盖,可以理解为合并顶点的动作,而匹配的点不重复(分出俩个点恰好对应路径覆盖时该店的一出一入),每成功一次匹配,则顶点集合少了一,即路径少了一条,所以最小路径覆盖对应最大匹配的时候,即证。

    该题(hdu3861),题意:划分一个有向图,要求:1,:同一个强连通分量(SCC)中的点属于一个集合,2:每个点只属于一个集合。3:任意俩个点都可以单向抵达的(不能越过其他集合)是属于一个集合。

    原图不是无环图,先缩点,成有向无环图,在求最下路径覆盖,按上面方法,代码有注解:

    (转载请注明出处。)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<stack>
    using namespace std;
    int n,m;
    const int MAX=50010*2;const int inf=0x3f3f3f3f;
    vector<vector<int> >v(MAX);
    int vis[MAX];int dfn[MAX];int low[MAX];
    int times=0;int scc[MAX];int ins[MAX];stack<int>s;
    int num=0;             //缩点后点数
    int e[MAX*3][3];int head[MAX*2];int nume=0;  
    void clea()    //初始化工作
    {
        for(int i=0;i<=2*n+1;i++)
        {
            head[i]=-1;
            ins[i]=dfn[i]=vis[i]=low[i]=scc[i]=0;
            v[i].clear();
        }
        num=0;times=0;nume=0;
    }
    void inline adde(int f,int s,int w) //添加新图(二分图)的边,用网络流法解,所用之
    {
        e[nume][0]=s;e[nume][1]=head[f];head[f]=nume;
        e[nume++][2]=w;
        e[nume][0]=f;e[nume][1]=head[s];head[s]=nume;
        e[nume++][2]=0;
    }
    void tarjan(int u)  //有向图缩点
    {
        dfn[u]=low[u]=++times;
        ins[u]=1;
        s.push(u);
        for(int i=0;i<v[u].size();i++)
        {
            int ch=v[u][i];
            if(!vis[ch])
            {
                vis[ch]=1;
                tarjan(ch);
                if(low[ch]<low[u])
                   low[u]=low[ch];
            }
            else
               if(ins[ch]&&dfn[ch]<low[u])
                  low[u]=dfn[ch];
        }
        if(low[u]==dfn[u])
        {
            int cur;
            num++;
            do
            {
                cur=s.top();
                s.pop();
                scc[cur]=num;
                ins[cur]=0;
            }while(cur!=u);
        }
    }
    void  get_newgraph() //获得新图,二分图
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<v[i].size();j++)
            {
                int c=v[i][j];
                if(scc[i]!=scc[c])
                {
                    adde(scc[i],scc[c]+num,1);
                }
            }
        }
        for(int i=1;i<=num;i++)
            {
                adde(0,i,1);
                adde(i+num,num+num+1,1);
            }
    }
    int lev[MAX];  //网络流法求最大匹配  添加源汇点(0,2*num+1),流量为1,有重边也不无纺。
    bool bfs()
    {
        for(int i=0;i<=num*2+1;i++)
        {
            lev[i]=vis[i]=0;
        }
        queue<int>q;
        q.push(0);vis[0]=1;
       while(!q.empty())
       {
           int cur=q.front();
           q.pop();
            for(int i=head[cur];i!=-1;i=e[i][1])
            {
                int c=e[i][0];
                if(!vis[c]&&e[i][2]>0)
                {
                    lev[c]=lev[cur]+1;
                    if(c==num*2+1)return 1;
                    vis[c]=1;
                    q.push(c);
                }
            }
       }
       return vis[num*2+1];
    }
    int dfs(int u,int minf)
    {
        if(u==num*2+1||minf==0)return minf;
        int sum=0,f;
        for(int i=head[u];i!=-1&&minf;i=e[i][1])
         {
                int c=e[i][0];
                if(lev[c]==lev[u]+1&&e[i][2]>0)
                {
                    f=dfs(c,minf<e[i][2]?minf:e[i][2]);
                    e[i][2]-=f;e[i^1][2]+=f;
                    sum+=f;minf-=f;
                }
         }
         return sum;
    }
    int dinic()
    {
        int sum=0;
        while(bfs())
        {
            sum+=dfs(0,inf);
        }
        return sum;
    }
    int main()
    {
       int ta;
       scanf("%d",&ta);
       while(ta--)
       {
          scanf("%d%d",&n,&m);
          int aa,bb;
          clea();
          for(int i=0;i<m;i++)
            {
                scanf("%d%d",&aa,&bb);
                v[aa].push_back(bb);
            }
           for(int i=1;i<=n;i++)
           {
               if(!vis[i])
                   {
                       vis[i]=1;
                       tarjan(i);
                   }
           }
           get_newgraph();
           int ans=num-dinic();  //最小路径覆盖数
           printf("%d
    ",ans);
       }
       return 0;
    }
    

  • 相关阅读:
    Camera
    iOS实现截屏 并合适保存
    将UIView转成UIImage,将UIImage转成PNG/JPG
    iOS7Status bar适配
    @synthesize obj=_obj的意义详解 @property和@synthesize
    iOS各种问题处理
    Foundation框架中的NSNumber对象详解
    iOS 文件和数据管理 (可能会删除本地文件储存)
    当ABAP遇见普罗米修斯
    一个工作13年的SAP开发人员的回忆:电子科技大学2000级新生入学指南
  • 原文地址:https://www.cnblogs.com/yezekun/p/3925718.html
Copyright © 2020-2023  润新知