二分图(图论)

use1:解决矩阵横纵坐标(或者抽象成这样的关系)相互关联,求最少约束方案数量

E

给出一张由方块组成的地图。方块有许多种:墙,草,和空地。老板想让Robert在地图上放置尽可能多的机器人。每个机器人拿着一把激光枪,它可以同时向东西南北四个方向射击。机器人必须一直呆在它开始时被放在的位置并且不断地射击。激光束当然可以经过空地或草地,但不能穿过墙。机器人只能被放在空地上。当然老板不希望看到机器人相互攻击。换句话说,两个机器人不能被放在一条线上(竖直或水平),除非它们中间有一堵墙。

由于你是一位机智的程序员和Robert的好基友之一,他请你帮他解决这个问题。也就是说,给出地图的描述,计算地图上最多能放置的机器人数量。

输入格式

输入文件的第一行有两个正整数m,n(1<=m,n<=50),即地图的行数和列数。

接下来有m行,每行n个字符,这些字符是'#','*'或'o',它们分别代表墙,草和空地。

输出格式

输出一行一个正整数,即地图中最多放置的机器人数目

横纵坐标为二分图两侧,对于墙也就意味着点不能够共享横纵坐标,分开就行

const int N=70;
struct node
{
    int xx, yy;
    node(){
    }
    node(int xxx,int yyy)
    {
        xx=xxx;yy=yyy;
    }
};
int x_add[N][N],y_add[N][N],n,m,x[N][N],y[N][N];
char s[N];
vector<node>pos;
struct node2
{
    int to,nxt;
}e[N*N];
int head[N*N],tot;
int max_x;
inline void add(int _x,int _y)
{
    e[++tot].to=_y,e[tot].nxt=head[_x],head[_x]=tot;
}
int mach[N*N],vis[N*N];
bool dfs(int x,int fa)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        int u=e[i].to;
        if(u==fa)continue;
        if(!vis[u])
        {
            vis[u]=1;
            if(!mach[u]||dfs(mach[u],u))
            {
                mach[u]=x;
                return true;
            }
        }
    }
    return false;
}
int siz=0;
void deal()
{

    _f(i,0,siz-1)
    {
        int x_p=x[pos[i].xx][pos[i].yy],y_p=y[pos[i].xx][pos[i].yy];
        add(x_p,y_p+max_x);
        add(y_p+max_x,x_p);//连边
    }
    //跑匈牙利
    int ans=0;
    _f(i,1,max_x)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i,0))ans++;
    }
     chu("%d",ans);
     return;
}
int main()
{
    //freopen("exam.txt","r",stdin);
    m=re(),n=re();
    _f(i,1,m)
    {
        scanf("%s",s+1);
        _f(j,1,n)
        {
            if(s[j]=='#')//是墙
            {
                x_add[i][j]+=1;
                y_add[i][j]+=1;
                //if(j==4)chu("y[%d][%d]+=1",i,j);
            }
            else if(s[j]=='o')
            {
                siz++;
               // chu("siz:%d\n",siz);
                pos.push_back(node(i,j));
            }
        }
    }
    //chu("%d\n",y_add[2][4]);
    //处理行位置坐标列加
    _f(i,1,m)//对于这一行
    {
        _f(j,1,n)
        {
            x_add[i][j]+=x_add[i][j-1];//前缀和
            x[i][j]=i+x_add[i][j];
        }
        x_add[i+1][1]+=x_add[i][n];//别忘了把上一行的累加传下去
    }
    max_x=m+x_add[m][n];
    _f(i,1,n)//对于每一列 ,i是列!!!
    {
        _f(j,1,m)//m是行!!
        {
            y_add[j][i]+=y_add[j-1][i];
            //if(i==4)chu("add[%d][%d]:%d(2,4 %d)\n",j,i,y_add[j][i],y_add[2][4]);

            y[j][i]=i+y_add[j][i];
            //if(i==4)chu("y[%d][%d]=%d+(add)%d\n",j,i,i,y_add[j][i]);
        }
        y_add[1][i+1]+=y_add[m][i];
    }

    //return 0;
    deal();
    return 0;
}

use2:解决求无向图最多孤立点问题,任意一点无边联通(有向图不知道)

是从一个无向图里找出几个点使得他们没有边联通

F猫和狗

F. 猫和狗 - 二分图 - 比赛 - 衡中OI (hszxoj.com)

把喜好有矛盾的两个人连一条边(没有环),跑一边匈牙利找最大匹配max(除2!)

ans=n(人的数量)-max,

就是最多选的人数,保证他们之间没有任意一条路径连通,一定不存在矛盾

use3:最小路径覆盖,有向无环图

最小路径覆盖:、

题目概述:

给你一个有向无环图,有n个点,m条有向边。

一个机器人可以从任意点开始沿着边的方向走下去。对于每一个机器人:走过的点不能再走过。

问你最少用几个机器人可以走完所有的n个点,不同的机器人可以走相同的点。

用尽量少的不相互交叉简单路径覆盖有向无环图G的所有结点(不交叉指的是原图,而非后来构造的二分图)。即覆盖点(同样地,在路径覆盖中单独一个点没有与它相连的边,也算作一次路径去覆盖这个点)。建立一个二分图模型,把所有顶点i拆成两个:X集中的i和Y集中的i',如果有边i->j,则在二分图中引入边i->j',结果就是最小路径覆盖 = N - 最大匹配数。(N为原图中结点数)

最小路径覆盖和最小边覆盖不同,不要求给的图是二分图,而是要求是N×N的有向图,且不能有环,然后根据原图构造二分图,构造方法是将点一分为二,如:i分为i`和i``然后如果i和j有边,那么就在i`和j``之间连一条边。由此构成二分图。

然后最小路径覆盖 = n-m,n为原图的点的个数,m为新造二分图的最大匹配。

若是题目要求可多次经过一个点的最小路径覆盖呢?

解决的办法是对原图进行一次闭包传递(通过Warshell传递闭包算法),于是便增加了边,然后再去求最小路径覆盖。

传递闭包概念:

一个有n个顶点的有向图的传递闭包为:有向图中的初始路径可达情况我们存入邻接矩阵A,邻接矩阵中A[i,j]表示i到j是否直接可达,若直接可达,则A[i,j]记为1,否则记为0;有向图中i到j有路径表示从i点开始经过其他点(或者不经过其他点)能够到达j点,如果i到j有路径,则将T[i,j]设置为1,否则设置为0;有向图的传递闭包表示从邻接矩阵A出发,求的是所有节点间的路径可达情况,该T矩阵就为所要求的传递闭包矩阵。(用floyed解决·)
最小路径覆盖问题求解及与最小边覆盖的区别_薄层的博客-CSDN博客_最小边覆盖

 

posted on 2022-05-04 16:05  HZOI-曹蓉  阅读(91)  评论(0)    收藏  举报