二分图常见模型

最小点覆盖

最小的点集使得其相连的边能覆盖所有边。

König定理:最小点覆盖=最大匹配

证明很简单,在得到最大匹配之后,一条边必然只有两种情况,两点都是匹配点,只有一个是匹配点。又因为对于一条匹配边来说,其两点上不可能同时连有非匹配点(即第二种情况的边),那么只需要把匹配边上两点的其中一点(含有第二种情况的点)包含到点集里,其另一点就可以通过选择的这个点来覆盖了(因为匹配边之间都是独立的)。

POJ 3041 Asteriods
给一个N*N的矩阵,有些格子有障碍,要求我们消除这些障碍,问每次消除一行或一列的障碍,最少要几次。

矩阵上每一个点只需要选中一次,那么对应从行部连到列部的一条边,把所有矩阵上的点消除就需要用最少的点来覆盖这些边,所以匈牙利跑一边即可。

类似的一道题:
UVA 11419 SAM I AM
二分图常见模型_第1张图片
二分图常见模型_第2张图片
二分图常见模型_第3张图片
只不过要输出方案,其他方法不好处理,那么可以用S T 数组记录访问到的点,那么求出最大匹配之后,再从X部的非匹配点出发进行匹配,此时不影响答案,但是过程中访问到的点是必须要选的,不然这个非匹配点无法覆盖,另外不用考虑访问过程中访问到Y部的非匹配点,因为这种情况根本不存在。然后对于Y部非匹配点同样不会从X部匹配点出发,所以这样找出X中没有访问过,Y中访问到的店即使答案。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 1005;
struct Edge
{
    int to,next;
}edge[maxn*maxn];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
}
int r,c,n;
bool init()
{
    if(!~scanf("%d%d%d",&r,&c,&n) || !(r|c|n)) return false;
    memset(head,-1,sizeof(head));
    maxedge = -1;
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    return true;
}
bool S[maxn],T[maxn];
int matchx[maxn],matchy[maxn];
bool dfs(int u)
{
    S[u]=true;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        if(T[v]) continue;
        T[v]=true;
        if(!matchy[v] || dfs(matchy[v]))
        {
            matchx[u]=v;
            matchy[v]=u;
            return true;
        }
    }
    return false;
}
int hungary()
{
    int ret=0;
    memset(matchx,0,sizeof(matchx));
    memset(matchy,0,sizeof(matchy));
    for(int i=1;i<=r;i++)
    {
        memset(S,0,sizeof(S));
        memset(T,0,sizeof(T));
        if(dfs(i)) ret++;
    }
    return ret;
}
void work()
{
    int ans = hungary();
    printf("%d",ans);
    memset(S,0,sizeof(S));
    memset(T,0,sizeof(T));
    for(int i=1;i<=r;i++) if(!matchx[i])
        dfs(i);
    for(int i=1;i<=r;i++) if(!S[i])
        printf(" r%d",i);
    for(int i=1;i<=c;i++) if(T[i])
        printf(" c%d",i);
    putchar('\n');
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("sam.in","r",stdin);
    freopen("sam.out","w",stdout);
    #endif
    while(init())
        work();
    return 0;
}

最小边覆盖

最小的边集使得其覆盖所有的点。

最小边覆盖=总点数-最大匹配

一条匹配边能覆盖两个点,而其他边只能覆盖非匹配点,也就是一个点,那么匹配边的贡献就是1,所以用总点数-最大匹配数即可。

马控制棋盘问题
已知n * m的棋盘,其中一些格子有障碍物。现要在棋盘上无障碍物的格子中布置一些马,每只马可以控制两个格子:一个是它所在的格子,另一个是它通过一步可以跳到的格子。马一步可以跳到的格子如下所示:
二分图常见模型_第4张图片
现在的问题是:至少要放多少只马才能将所有无障碍物的格子控制?

对于这道题,可以先将棋盘染色
二分图常见模型_第5张图片
那么发现马只能连接不同颜色的两个格子,那么把需要的边建好之后,求出最小的边使得所有非障碍点都被守住即可。。


最大独立集

最大的点集使得没有边连接相邻的点。

最大独立集=最小边覆盖=总点数-最大匹配

原理和上一个类似。。由于独立集合覆盖集互补,且最大匹配可以解覆盖集,所以最大匹配可以解独立集。。

UVAlive 3415 Guardian of Decency
二分图常见模型_第6张图片
二分图常见模型_第7张图片
二分图常见模型_第8张图片
二分图常见模型_第9张图片
这位老师有点变态。。
取最多的学生使得两两没有暧昧关系。。。。
那么二分图最大独立集求解。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 505;
struct Edge
{
    int to,next;
}edge[maxn*maxn];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
}
#define maxlen 105
struct Data
{
    int height;
    unsigned music,sport;
    bool operator == (const Data t) const
    {
        return abs(height-t.height)<=40 && music==t.music && sport^t.sport; // sport is different!!
    }
}M[maxn],F[maxn];
inline unsigned hash(char s[])
{
    unsigned ret=0u;
    int lens = strlen(s);
    for(int i=0;i5) ^ (ret>>27) ^ s[i];
    return ret;
}
int n,m,f;
char s[maxlen];
void init()
{
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    maxedge=-1;
    m=f=0;
    for(int i=1;i<=n;i++)
    {
        int h;
        char sex;
        scanf("%d",&h);
        sex=getchar();
        while(sex^'M' && sex^'F') sex=getchar();
        int *p;
        Data *arr;
        if(sex=='M') p=&m,m++,arr=M;
        else p=&f,f++,arr=F;
        scanf("%s",s);
        arr[*p].height=h;
        arr[*p].music=hash(s);
        scanf("%s",s);
        arr[*p].sport=hash(s);
    }
    for(int i=1;i<=m;i++)
        for(int j=1;j<=f;j++)
            if(M[i]==F[j]) addedge(i,j);
}
int match[maxn];
bool vis[maxn];
bool dfs(int u)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]) continue;
        vis[v]=true;
        if(!match[v] || dfs(match[v]))
        {
            match[v]=u;
            return true;
        }
    }
    return false;
}
int hungary()
{
    int ret=0;
    memset(match,0,sizeof(match));
    for(int i=1;i<=m;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ret++;
    }
    return ret;
}
int work()
{
    int ans=hungary();
    return n-ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("decency.in","r",stdin);
    freopen("decency.out","w",stdout);
    #endif
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        init();
        printf("%d\n",work());
    }
    return 0;
}

最小路径覆盖

对于DAG,用最小的路径个数覆盖所有的点。

第一种路径不相交。。
UVAlive 3126 Taxi Cab Scheme
二分图常见模型_第10张图片
二分图常见模型_第11张图片
由于一条路径上只有一个无后继点,那么一条路径就可以看成一个无后继点,如果原图有i->j 那么在新图上建立 i->j’ 这样求出最大匹配之后,剩下的都是非匹配点,那么答案就是 总点数-最大匹配。。
如果一辆车能在完成 i 订单之后 还可以接 j 订单,那么连边 i->j 即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 505;
struct Edge
{
    int to,next;
}edge[maxn*maxn];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
}
struct Order
{
    int T;
    int sx,sy;
    int tx,ty;
}order[maxn];
inline int dist(int x1,int y1,int x2,int y2) { return abs(x1-x2) + abs(y1-y2); }
inline bool check(int i,int j)
{
    int dis1=dist(order[i].sx,order[i].sy,order[i].tx,order[i].ty);
    int dis2=dist(order[i].tx,order[i].ty,order[j].sx,order[j].sy);
    return order[i].T+dis1+dis2 < order[j].T;
}
int n;
void init()
{
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    maxedge=-1;
    for(int i=1;i<=n;i++)
    {
        int &T=order[i].T;
        char ch=getchar();
        while(ch<'0' || ch>'9') ch=getchar();
        T=ch-'0';
        ch=getchar();
        T*=10; T+=ch-'0'; T*=60;
        getchar();
        ch=getchar();
        T+=10*(ch-'0');
        ch=getchar();
        T+=ch-'0';
        scanf("%d%d%d%d",&order[i].sx,&order[i].sy,&order[i].tx,&order[i].ty);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(check(i,j)) addedge(i,j);
}
int match[maxn];
bool vis[maxn];
bool dfs(int u)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]) continue;
        vis[v]=true;
        if(!match[v] || dfs(match[v]))
        {
            match[v]=u;
            return true;
        }
    }
    return false;
}
int hungary()
{
    int ret=0;
    memset(match,0,sizeof(match));
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ret++;
    }
    return ret;
}
int main()
{
    freopen("cab.in","r",stdin);
    freopen("cab.out","w",stdout);
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        init();
        int ans = hungary();
        printf("%d\n",n-ans);
    }
    return 0;
}

第二种是可以相交的情况,那么需要用Floyed传递闭包,这样可以保证有经过重复点的可能性。

POJ 2594 Treasure Exploration

Description
Have you ever read any book about treasure exploration? Have you ever see any film about treasure exploration? Have you ever explored treasure? If you never have such experiences, you would never know what fun treasure exploring brings to you.
Recently, a company named EUC (Exploring the Unknown Company) plan to explore an unknown place on Mars, which is considered full of treasure. For fast development of technology and bad environment for human beings, EUC sends some robots to explore the treasure.
To make it easy, we use a graph, which is formed by N points (these N points are numbered from 1 to N), to represent the places to be explored. And some points are connected by one-way road, which means that, through the road, a robot can only move from one end to the other end, but cannot move back. For some unknown reasons, there is no circle in this graph. The robots can be sent to any point from Earth by rockets. After landing, the robot can visit some points through the roads, and it can choose some points, which are on its roads, to explore. You should notice that the roads of two different robots may contain some same point.
For financial reason, EUC wants to use minimal number of robots to explore all the points on Mars.
As an ICPCer, who has excellent programming skill, can your help EUC?

Input
The input will consist of several test cases. For each test case, two integers N (1 <= N <= 500) and M (0 <= M <= 5000) are given in the first line, indicating the number of points and the number of one-way roads in the graph respectively. Each of the following M lines contains two different integers A and B, indicating there is a one-way from A to B (0 < A, B <= N). The input is terminated by a single line with two zeros.

Output
For each test of the input, print a line containing the least robots needed.

Sample Input
1 0
2 1
1 2
2 0
0 0
Sample Output
1
1
2

传递时要注意剪枝。。。顺序可以不同!

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 505;
struct Edge
{
    int to,next;
}edge[maxn*maxn];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
}
int n,m;
bool g[maxn][maxn];
void Floyed()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(!g[i][j]) // cut1
                for(int k=1;k<=j;k++)
                    if(g[i][k]&g[k][j])
                    {
                        g[i][j]=true;
                        break; //cut2
                    }
}
inline bool init()
{
    if(!~scanf("%d%d",&n,&m) || !(n|m)) return false;
    memset(head,-1,sizeof(head));
    maxedge=-1;
    memset(g,0,sizeof(g));
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        g[x][y]=true;
    }
    Floyed();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i^j && g[i][j]) addedge(i,j);
    return true;
}
int match[maxn];
bool vis[maxn];
bool dfs(int u)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(vis[v]) continue;
        vis[v]=true;
        if(!match[v] || dfs(match[v]))
        {
            match[v]=u;
            return true;
        }
    }
    return false;
}
int hungary()
{
    int ret=0;
    memset(match,0,sizeof(match));
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ret++;
    }
    return ret;
}
int main()
{
    freopen("exp.in","r",stdin);
    freopen("exp.out","w",stdout);
    while(init())
        printf("%d\n",n-hungary());
    return 0;
}

你可能感兴趣的:(图论,二分图,二分图匹配,hash)