• poj 3020 Antenna Placement (最小路径覆盖)


      二分图题目

      当时看到网上有人的博客写着最小边覆盖,也有人写最小路径覆盖,我就有点方了,斌哥(kuangbin)的博客上只给了代码,没有解释,但是现在我还是明白了,这是个最小路径覆盖(因为我现在还不知道啥叫最小边覆盖)。

      有一篇博客如下写道:最小路径覆盖只对有向无环图而言,且并不要求原图是二分图,给所有点一个分身,让他们分别处于两个集合就可以,求出的最小路径覆盖 = n - 最大匹配值。

      证明:假设最大匹配值是0,那原先一共有n个路径,每次多一个匹配,这样的路径就减少1,证明完毕。

      那么回到这个题,这个题到底是有向图还是无向图呢?其实这取决于你的建图方式,这个图的难点也是建图,我先说下我的建图经历:

    一开始我是想把一个点(x,y)化成一位坐标系X*m+Y,建立一个无向图,然后使用复制的方法后来发现这种方式是不太好的,因为它浪费了很多的空间,而且日狗的是还出现了莫名其妙的错误,所以我也决定不粘贴自己的代码了,于是我开始求助斌哥,看他博客里采用了一种hash的思想,我感觉是非常好的,他使用的是无向图的建立方式,然后最小路径覆盖 = n - 最大匹配值 / 2,因为复制之后的匹配,每个边都被匹配了两次,这个自己画一下就能看出来。这个方法的正确性毫无置疑,再次附上斌哥的博客地址以及代码。http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int MAXN=510;
    int uN,vN;//u,v数目
    int g[MAXN][MAXN];
    int linker[MAXN];
    bool used[MAXN];
    bool dfs(int u)//从左边开始找增广路径
    {
        int v;
        for(v=0; v<vN; v++) //这个顶点编号从0开始,若要从1开始需要修改
            if(g[u][v]&&!used[v])
            {
                used[v]=true;
                if(linker[v]==-1||dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }
            }
        return false;//这个不要忘了,经常忘记这句
    }
    int hungary()
    {
        int res=0;
        int u;
        memset(linker,-1,sizeof(linker));
        for(u=0; u<uN; u++)
        {
            memset(used,0,sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    char map[50][50];
    int hash[50][50];
    int main()
    {
        int T;
        int h,w;
        scanf("%d",&T);
        int tol;
        while(T--)
        {
            scanf("%d%d",&h,&w);
            tol=0;
            for(int i=0; i<h; i++)
            {
                scanf("%s",&map[i]);
                for(int j=0; j<w; j++)
                    if(map[i][j]=='*')
                        hash[i][j]=tol++;
            }
            memset(g,0,sizeof(g));
            for(int i=0; i<h; i++)
                for(int j=0; j<w; j++)
                    if(map[i][j]=='*')
                    {
                        if(i>0&&map[i-1][j]=='*')g[hash[i][j]][hash[i-1][j]]=1;
                        if(i<h-1&&map[i+1][j]=='*') g[hash[i][j]][hash[i+1][j]]=1;
                        if(j>0&&map[i][j-1]=='*')  g[hash[i][j]][hash[i][j-1]]=1;
                        if(j<w-1&&map[i][j+1]=='*')  g[hash[i][j]][hash[i][j+1]]=1;
                    }
            uN=vN=tol;
            printf("%d
    ",tol-hungary()/2);
        }
        return 0;
    }
    View Code

      那么有向图怎么建呢,某位大牛说很难建,因为还要判断这个边是否出现过,但其实并没有那么复杂,我们采用根据(i+j)的奇偶性的建图,建出来的就是一个有向图,因为与他相连的点奇偶性肯定与他不同,所以这样建图是正确的,想到这种方法的大神就是把这个题说成是最小边覆盖的人,我附上他的博客地址以及代码。http://blog.csdn.net/y990041769/article/details/37900991

      其实要分清有向图和无向图的最小路径覆盖也很简单,思考这样一个问题,有向图1->2->3,匹配是2,无向图1 - 2 - 3,把它当成有向图去考虑,假如我们把12匹配了,23就不能匹配了,因为无向图就是一个双向的有向图,既然选择这条边,那么就选择了正反向两条边,如果在选23,那么2这个点的入度和出度不是1了,也就不是正确的匹配了~

      最后针对奇偶性建图再说一句,我更加推荐这种方法,比较的有思维水平,在这种建图方式中不会出现连指向,奇偶必须间隔,所以每个路径只能含有一条边,从而也验证了该种方式的正确性。

    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const int N = 1200;
    #define Del(x,y) memset(x,y,sizeof(x))
    int map[N][N],link[N],vis[N],vlink[N];
    char path[50][50];
    int line[50][50];
    int n,m,t,tmp1,tmp2;
    bool dfs(int x)
    {
        for(int i=1; i<tmp2; i++)
        {
            if(map[x][i]==1 && vis[i]==0)
            {
                vis[i]=1;
                if(link[i]==-1 || dfs(link[i]))
                {
                    link[i]=x;
                    return true;
                }
            }
        }
        return false;
    }
    void solve()
    {
        int ans=0;
        Del(link,-1);
        for(int i=1; i<tmp1; i++)
        {
            Del(vis,0);
            if(dfs(i))
                ans++;
        }
        //printf("%d
    ",ans);
        printf("%d
    ",tmp1+tmp2-ans-2);
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        //freopen("Input.txt","r",stdin);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            Del(path,0);
            for(int i=1;i<=n;i++)
            {
                getchar();
                for(int j=1;j<=m;j++)
                    scanf("%c",&path[i][j]);
            }
            Del(line,-1);
            tmp1=1,tmp2=1;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    if(path[i][j]=='*')
                    {
                        if((i+j)%2==0)
                            line[i][j]=tmp1++;
                        else
                            line[i][j]=tmp2++;
                    }
                }
            }
    
            Del(map,0);
            for(int i=1; i<=n; i++)
            {
                for(int j=1; j<=m; j++)
                {
                    if(path[i][j]=='*' && (i+j)%2==1)
                    {
                        if(line[i-1][j]>=1)
                            map[line[i-1][j]][line[i][j]]=1;
                        if(line[i+1][j]>=1)
                            map[line[i+1][j]][line[i][j]]=1;
                        if(line[i][j-1]>=1)
                            map[line[i][j-1]][line[i][j]]=1;
                        if(line[i][j+1]>=1)
                            map[line[i][j+1]][line[i][j]]=1;
                    }
                }
            }
            solve();
        }
        return 0;
    }
  • 相关阅读:
    2018 ACM-ICPC 中国大学生程序设计竞赛暨丝绸之路程序设计竞赛
    牛客小白月赛2
    求n个数中前k个数,按之前的顺序输出(HK IPM)
    js_复选框单选与复选
    js_判断字符串中出现最多的字符的和次数
    js_倒计时
    海通证券面试
    上海利莫面试
    中焯信息面试
    富途面试
  • 原文地址:https://www.cnblogs.com/jifahu/p/5523137.html
Copyright © 2020-2023  润新知