• poj2226-Muddy Fields二分匹配 最小顶点覆盖 好题


    题目

    给到一个矩阵,有些格子上是草,有些是水。需要用宽度为1,长度任意的若干块木板覆盖所有的水,并不能覆盖草,木板可以交叉,但只能横竖放置,问最少要多少块板。

    分析

    经典的矩阵二分图构图和最小点覆盖。
    无非就是两种方向,横向和竖向。我们把水块连续的编成同一号,那么对于一个点,它会有一个横向编号和纵向编号。我们要覆盖这个点,只需要覆盖这条边即可。于是问题转化成了一个最小点覆盖问题,即在二分图上选出最少的点,使它们能够覆盖所有的边。这里引出König定理。

    König 定理

    二分图中,最小点覆盖=最大匹配.

    是不是感觉跟"最小割=最大流"有点像......
    可参看最大最小定理

    证明

    匈牙利算法的流程表明,一个最大匹配满足从任意一个未匹配点出发,都无法找到一条增广路径。假设最大匹配数为MM.
    对于右边的所有未匹配点,我们寻找它们的所有交替路(未匹配——匹配交替出现),并把这个交替路上(包括自己)的所有点打上标记,那么最小点覆盖的点集为:左边有标记的点+右边无打标记的点,点集大小为MM.
    接下来有三个问题:

    • 为什么点集大小为MM?
    • 为什么这个点集可以覆盖所有边?
    • 为什么这个点集是最小的?

    下面将按顺序证明。

    • 上述点集为中每个点都是一条匹配边的某一个顶点,所以点集大小与匹配边数相等。按照上述画法,若右边一个点没有匹配过,那么它会被打上标记;若左边一个点没有匹配过,那么走不到这个点,否则将是一条新增广路。因此右边无标记点在匹配边上,左边有标记点在匹配边上。又因为不可能出现一条匹配边右边无标记而左边有标记(这种情况下左端点可以通过匹配边走到右端点给它打上标记),所以这种计算方法不会算重复,故每个点可以对应一条匹配边,点集大小为匹配边数M
    • 不存在一条边,它的左边无标记,右边有标记。一条边可以被覆盖当且仅当左右其中一个端点在点集中。只要证明不存在一条边的左右端点均不在点集中即可,即证明不存在一条边的左端点无标记,右端点有标记。若这条边是匹配边,那么右端点不可能作为上述交替路的起点,所以标记是从左端点来到,故左端点会有标记;若这条边不是匹配边,那么右端点肯定会被选作交替路起点,从而得到标记。所以不存在一条边,它的左边无标记,右边有标记。
    • 一个点覆盖必须覆盖所有的匹配边,而匹配边的数量为M,没有更小的情况。由于一个点不可能连出两条匹配边(不符合匹配的定义),所以要覆盖M条匹配边,需要至少M个点。我们已经构造出了这种方案。

    综上,最小点覆盖数=最大匹配数。

    这类题的建图方法:

    把矩阵作为一个二分图,以行列分别作为2个顶点集

    首先以每一行来看,把这一行里面连续的*编号,作为一个顶点

    再以每一列来看,把这一列里面连续的*编号,作为一个顶点

    则每一个*都有2个编号,以行看时有一个,以列看时有一个,则把这2个编号连边,容量为1

    再建一个源点,连接所有行的编号,一个汇点,连接所有列的编号

    这道题要求的是,所有*都被覆盖,即找到一个顶点的集合S,使得任意边都有至少一个顶点属于

    S,即求一个点集顶点覆盖S,又要木板数最少,所以求的就是最小顶点覆盖。

    最小顶点覆盖怎么求?

    二分图中,有:

    最小顶点覆盖=最大匹配

    所以这道题就转化为求二分图的最大匹配了

    再转化为最大流dinic算法。

    dinic算法

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    
    using namespace std;
    
    const int maxn=2510;
    const int inf=0x3f3f3f3f;
    const int s=0;
    int t;
    int tota;
    int totb;
    
    inline int min(int x,int y)
    {
        return x<y?x:y;
    }
    
    struct Edge
    {
        int to,cap,rev;
    };
    vector<Edge>edge[maxn];
    int iter[maxn];
    int level[maxn];
    char str[55][55];
    int hash[55][55];
    
    void addedge(int from,int to,int cap)
    {
        edge[from].push_back((Edge){to,cap,edge[to].size()});
        edge[to].push_back((Edge){from,0,edge[from].size()-1});
    }
    
    void build_graph(int n,int m)
    {
        for(int i=0;i<n*m;i++)
            edge[i].clear();
        tota=0;
        for(int i=1;i<=n;i++)
        {
            int j=1;
            while(j<=m)
            {
                if(str[i][j]=='*')
                {
                    tota++;
                    hash[i][j]=tota;
                    while(j<=m&&str[i][j+1]=='*')
                    {
                        j++;
                        hash[i][j]=tota;
                    }
                }
                j++;
            }
        }
        totb=tota;
        for(int j=1;j<=m;j++)
        {
            int i=1;
            while(i<=n)
            {
                if(str[i][j]=='*')
                {
                    totb++;
                    addedge(hash[i][j],totb,1);
                    while(i<=n&&str[i+1][j]=='*')
                    {
                        i++;
                        addedge(hash[i][j],totb,1);
                    }
                }
                i++;
            }
        }
        t=tota+totb+1;
        for(int i=1;i<=tota;i++)
            addedge(s,i,1);
        for(int i=tota+1;i<=totb;i++)
            addedge(i,t,1);
    }
    
    void bfs()
    {
        memset(level,-1,sizeof level);
        queue<int>que;
        while(!que.empty())
            que.pop();
        que.push(s);
        level[s]=1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            for(int i=0;i<edge[u].size();i++)
            {
                Edge &e=edge[u][i];
                if(e.cap>0&&level[e.to]<0)
                {
                    level[e.to]=level[u]+1;
                    que.push(e.to);
                }
            }
        }
    }
    
    int dfs(int u,int f)
    {
        if(u==t)
            return f;
        for(int &i=iter[u];i<edge[u].size();i++)
        {
            Edge &e=edge[u][i];
            if(e.cap>0&&level[e.to]>level[u])
            {
                int d=dfs(e.to,min(f,e.cap));
                if(d>0)
                {
                    e.cap-=d;
                    edge[e.to][e.rev].cap+=d;
                    return d;
                }
    
            }
        }
        return 0;
    }
    
    int solve()
    {
        int flow=0;
        while(true)
        {
            bfs();
            if(level[t]<0)
                return flow;
            memset(iter,0,sizeof iter);
            int f;
            while(f=dfs(s,inf))
            {
                flow+=f;
            }
        }
    }
    
    int main()
    {
        int n,m;
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=1;i<=n;i++)
            {
                scanf("%s",str[i]+1);
            }
            build_graph(n,m);
            printf("%d
    ",solve());
        }
        return 0;
    }
    View Code

    匈牙利算法

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int maxn=55;
    const int maxc=1e3+10;
    bool f[maxc][maxc],alr[maxc];
    int match[maxc],row[maxn][maxn],col[maxn][maxn],lid=0,rid=0,n,m,ans=0;
    char s[maxn][maxn];
    bool dfs(int x) {
        for (int i=1;i<=rid;++i) if (!alr[i] && f[x][i]) {
            alr[i]=true;
            if (!match[i] || dfs(match[i])) {
                match[i]=x;
                return true;
            }
        }
        return false;
    }
    int main() {
    
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;++i) scanf("%s",s[i]+1);
        for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (s[i][j]=='*') {
            ++lid;
            while (j<=m && s[i][j]=='*') row[i][j++]=lid;
            --j;
        }
        for (int j=1;j<=m;++j) for (int i=1;i<=n;++i) if (s[i][j]=='*') {
            ++rid;
            while (i<=n && s[i][j]=='*') col[i++][j]=rid;
            --i;
        }
        for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (s[i][j]=='*') f[row[i][j]][col[i][j]]=true;
        // for(int i = 1;i <= n; i ++)for(int j = 1;j <= m;j ++){
        //     cout << f[row[i][j]][col[i][j]] << " " ;
        // }
        for (int i=1;i<=lid;++i) memset(alr,0,sizeof alr),ans+=dfs(i);
        printf("%d
    ",ans);
    }
    View Code
  • 相关阅读:
    UML的相关基础知识
    easyui的datagrid、treegrid增加表头菜单,用于显示或隐藏列
    easyui datagrid load的时候,提交的时候多了name为数字的参数
    Easyui Dialog 设置初始位置
    投票系统的代码总结
    部门组织机构树的使用
    TCP连接客户端的方法
    播放M3U8的js代码
    JAVA的split的用法
    BOLB转word文件,和word文件转换BOLB
  • 原文地址:https://www.cnblogs.com/DWVictor/p/11348336.html
Copyright © 2020-2023  润新知