二分图小结

1.hdu1045

题意:给出一张图,图中‘X‘表示wall,‘.‘表示空地,可以放置blockhouse

同一条直线上只能有一个blockhouse,除非有wall隔开,问在给出的图中

最多能放置多少个blockhouse

每个边界与X都可以看成是点,.是边连的是离它最近的上面左面的两个边或者X

//二分图匹配(Hopcroft-Carp的算法)。
/*
初始化:g[][]邻接矩阵

调用:res=MaxMatch();  Nx,Ny要初始化!!!

时间复杂大为 O(V^0.5 E)

适用于数据较大的二分匹配

需要queue头文件

********************************************** */
#include<iostream>
#include<string.h>
#include<queue>
#include<stdio.h>
#include<vector>
using namespace std;
const int MAXN=3000;

const int INF=1<<28;

int Mx[MAXN],My[MAXN],Nx,Ny;
vector<int> g[MAXN];
int dx[MAXN],dy[MAXN],dis;

bool vst[MAXN];

bool searchP()

{

    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;i++)

        if(Mx[i]==-1)

        {

            Q.push(i);

            dx[i]=0;

        }

    while(!Q.empty())

    {

        int u=Q.front();

        Q.pop();

        if(dx[u]>dis)  break;

        for(int i = 0; i < g[u].size(); i++){
            int v = g[u][i];
            if(dy[v]==-1)

            {

                dy[v]=dx[u]+1;

                if(My[v]==-1)  dis=dy[v];

                else

                {

                    dx[My[v]]=dy[v]+1;

                    Q.push(My[v]);

                }

            }
        }

    }

    return dis!=INF;

}

bool DFS(int u)

{
    for(int i = 0; i < g[u].size(); i++){
        int v = g[u][i];
        if(!vst[v]&&dy[v]==dx[u]+1)

           {

               vst[v]=1;

               if(My[v]!=-1&&dy[v]==dis) continue;

               if(My[v]==-1||DFS(My[v]))

               {

                   My[v]=u;

                   Mx[u]=v;

                   return 1;

               }

           }
    }
    return 0;

}

int MaxMatch()

{

    int res=0;

    memset(Mx,-1,sizeof(Mx));

    memset(My,-1,sizeof(My));

    while(searchP())

    {

        memset(vst,0,sizeof(vst));

        for(int i=0;i<Nx;i++)

          if(Mx[i]==-1&&DFS(i))  res++;

    }

    return res;

}
const int maxa = 40;
char mp[maxa][maxa];
int num[maxa][maxa];
int main(){
    int n;
    while(scanf("%d", &n), n){
        for(int i = 0; i <= n; i++){
            for(int k = 0; k <= n; k++){
                mp[i][k] = ‘X‘;
            }
        }
        for(int i = 1; i <= n; i++){
            scanf("%s", mp[i]+1);
        }
        /*for(int i = 0; i <= n; i++){
            printf("%s\n", mp[i]);
        }*/
        int nn = 0;
        for(int i = 0;i <= n; i++){
            for(int k = 0; k <= n; k++){
                if(mp[i][k] == ‘X‘)
                    num[i][k] = nn++;
            }
        }
        for(int i = 0; i < nn; i++){
            g[i].clear();
        }
        for(int i = 1 ;i <= n; i++){
            for(int k = 1; k <= n; k++){
                int x, y;
                if(mp[i][k] == ‘.‘){
                    for(int j = k-1; j >= 0; j--){
                        if(mp[i][j] == ‘X‘){
                            x = num[i][j];
                            break;
                        }
                    }
                    for(int j = i-1; j >= 0; j--){
                        if(mp[j][k] == ‘X‘){
                            y = num[j][k];
                            break;
                        }
                    }
                    //printf("%d %d\n", x, y);
                    g[x].push_back(y);
                }
            }
        }
        Nx = nn;
        printf("%d\n", MaxMatch());
    }
}

2.hdu3605

题意:有 n 个人选择 m 个星球居住,选择情况和星球居住上限有限制,问是否能全部满足要求。
状态压缩+二分图

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define MAXM 555555
#define MAXN 2222
struct Edge{
    int v,cap,next;
}edge[MAXM];

int pre[MAXN];
int cur[MAXN];
int head[MAXN];
int level[MAXN];
int gap[MAXN];
int n,m;
int NV,NE;
void init(){
    NE = 0;
    memset(head, -1, sizeof(head));
}
int SAP(int vs,int vt){
    memset(pre,-1,sizeof(pre));
    memset(level,0,sizeof(level));
    memset(gap,0,sizeof(gap));
    for(int i=0;i<=NV;i++)cur[i]=head[i];
    int u=pre[vs]=vs,maxflow=0,aug=-1;
    gap[0]=NV;
    while(level[vs]<NV){
loop:
        for(int &i=cur[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(edge[i].cap&&level[u]==level[v]+1){
                aug==-1?aug=edge[i].cap:(aug=min(aug,edge[i].cap));
                pre[v]=u;
                u=v;
                if(v==vt){
                    maxflow+=aug;
                    for(u=pre[u];v!=vs;v=u,u=pre[u]){
                        edge[cur[u]].cap-=aug;
                        edge[cur[u]^1].cap+=aug;
                    }
                    aug=-1;
                }
                goto loop;
            }
        }
        int minlevel=NV;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(edge[i].cap&&minlevel>level[v]){
                cur[u]=i;
                minlevel=level[v];
            }
        }
        gap[level[u]]--;
        if(gap[level[u]]==0)break;
        level[u]=minlevel+1;
        gap[level[u]]++;
        u=pre[u];
    }
    return maxflow;
}

void addedge(int u,int v,int cap,int cc=0){

    //printf("*%d %d %d\n", u, v, cap);
    edge[NE].cap=cap;edge[NE].v=v;
    edge[NE].next=head[u];head[u]=NE++;

    edge[NE].cap=cc;edge[NE].v=u;
    edge[NE].next=head[v];head[v]=NE++;
}

int num[(1<<11) + 100];
int main(){
    int n, m;
    while(scanf("%d%d", &n, &m)!=EOF){          //将所有的点压缩到1~(1<<m) - 1  num 表示这样的人有多少
        init();
        memset(num, 0, sizeof(num));
        for(int i =0;i < n; i++){
            int id = 0;
            for(int k = 0; k < m; k++){
                int x;
                scanf("%d", &x);
                if(x){
                    id += 1<<k;
                }
            }
            num[id] ++;
        }
        for(int i = 0;i < m; i++){
            int x;
            scanf("%d", &x);
            num[(1<<m) + i] = x;                //星球的点变成(1<<m) + i num表示每个星球能够承载的人数
        }
        int src = (1<<m) +m + 1;
        int sink = (1<<m) + m +2;
        NV = sink+1;
        for(int i = 0;i < (1<<m); i++){
            addedge(src, i, num[i]);                //从起点到每种人建立一条人数的边
            for(int k = 0; k < m;k ++){
                if((1<<k) & i){
                    addedge(i,(1<<m) + k, num[i]);          //每种人到对应的每个星球都建立一条人数的边
                }
            }
        }
        for(int i =0 ;i < m; i++){
            addedge(i+(1<<m), sink, num[i+(1<<m)]);     //星球到终点建立一条星球能承载最大人数的边
        }
        //printf("%d\n", SAP(src, sink));
        if(SAP(src, sink) == n) printf("YES\n");
        else printf("NO\n");
    }
}

3.hdu 5093

//二分图匹配(Hopcroft-Carp的算法)。
/*
初始化:g[][]邻接矩阵

调用:res=MaxMatch();  Nx,Ny要初始化!!!

时间复杂大为 O(V^0.5 E)

适用于数据较大的二分匹配

需要queue头文件

********************************************** */
#include<iostream>
#include<string.h>
#include<queue>
#include<stdio.h>
#include<vector>
using namespace std;
const int MAXN=3000;

const int INF=1<<28;

int Mx[MAXN],My[MAXN],Nx,Ny;
vector<int> g[MAXN];
int dx[MAXN],dy[MAXN],dis;

bool vst[MAXN];

bool searchP()

{

    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;i++)

        if(Mx[i]==-1)

        {

            Q.push(i);

            dx[i]=0;

        }

    while(!Q.empty())

    {

        int u=Q.front();

        Q.pop();

        if(dx[u]>dis)  break;

        for(int i = 0; i < g[u].size(); i++){
            int v = g[u][i];
            if(dy[v]==-1)

            {

                dy[v]=dx[u]+1;

                if(My[v]==-1)  dis=dy[v];

                else

                {

                    dx[My[v]]=dy[v]+1;

                    Q.push(My[v]);

                }

            }
        }

    }

    return dis!=INF;

}

bool DFS(int u)

{
    for(int i = 0; i < g[u].size(); i++){
        int v = g[u][i];
        if(!vst[v]&&dy[v]==dx[u]+1)

           {

               vst[v]=1;

               if(My[v]!=-1&&dy[v]==dis) continue;

               if(My[v]==-1||DFS(My[v]))

               {

                   My[v]=u;

                   Mx[u]=v;

                   return 1;

               }

           }
    }
    return 0;

}

int MaxMatch()

{

    int res=0;

    memset(Mx,-1,sizeof(Mx));

    memset(My,-1,sizeof(My));

    while(searchP())

    {

        memset(vst,0,sizeof(vst));

        for(int i=0;i<Nx;i++)

          if(Mx[i]==-1&&DFS(i))  res++;

    }

    return res;

}
const int maxa = 400;
char mp[maxa][maxa];
int num[maxa][maxa];
int main(){
    int n, m;
    int t;
    scanf("%d", &t);
    while(t--){
        scanf("%d%d", &n, &m);
        for(int i = 0; i <= n; i++){
            for(int k = 0; k <= m; k++){
                mp[i][k] = ‘#‘;
            }
        }
        for(int i = 1; i <= n; i++){
            scanf("%s", mp[i]+1);
        }
        /*for(int i = 0; i <= n; i++){
            printf("%s\n", mp[i]);
        }*/
        int nn = 0;
        for(int i = 0;i <= n; i++){
            for(int k = 0; k <= m; k++){
                if(mp[i][k] == ‘#‘)
                    num[i][k] = nn++;
            }
        }
        for(int i = 0; i < nn; i++){
            g[i].clear();
        }
        for(int i = 1 ;i <= n; i++){
            for(int k = 1; k <= m; k++){
                int x, y;
                if(mp[i][k] == ‘*‘){
                    for(int j = k-1; j >= 0; j--){
                        if(mp[i][j] == ‘#‘){
                            x = num[i][j];
                            break;
                        }
                    }
                    for(int j = i-1; j >= 0; j--){
                        if(mp[j][k] == ‘#‘){
                            y = num[j][k];
                            break;
                        }
                    }
                    //printf("%d %d\n", x, y);
                    g[x].push_back(y);
                }
            }
        }
        Nx = nn;
        printf("%d\n", MaxMatch());
    }
}

时间: 2024-10-11 16:53:41

二分图小结的相关文章

ACM -二分图题目小结(更新中)

暂时只包括与最大匹配相关的问题. 求最大独立集,最小路径覆盖等等大多数题目都可以转化为求最大匹配用匈牙利算法解决. 1.最大匹配(边集) 此类问题最直接,直接用匈牙利算法即可. HDU 2063  过山车 http://acm.hdu.edu.cn/showproblem.php?pid=2063 二分图最大匹配模版题. ZOJ 1654 - Place the Robots http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode

二分图练习小结

1. HDU 1151 Air Raid 题目描述有点长,就是说一个有向无环图,最少需要走几次能把整张图上的边遍历.典型的最小路径覆盖,答案就是顶点数-最大匹配数 #include <set> #include <map> #include <list> #include <stack> #include <queue> #include <ctime> #include <cmath> #include <cstd

小结:二分图匹配

概要: 可以用匈牙利或者网络流(听说Dinic是O(sqrt(V)*E),isap我不知道,大概一样吧.) 应用: 最大匹配.最小点覆盖.最大独立集.最小路径覆盖.二分图完美匹配等. 技巧及注意: 匈牙利是O(nm)的,比网络流慢. KM比网络流慢. (还是写网络流吧...) 在X集和Y集元素不同时,又要求哪些X集中元素有且只有一个Y集中的元素与之匹配时,可以先跑二分图匹配,然后依次删边,看是否还能继续匹配,如果不能就是一个答案.例如:[wikioi]1222 信与信封问题(二分图+特殊的技巧)

二分图学习小结

二分图的性质:在无向图G中,至少要有两个点.如果存在回路,那么回路必为偶数边的回路.. 匹配: 在图论中,一个匹配是一个边的集合,其中任意两条边都没有公共顶点. 最大匹配: 一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配 最大匹配数:最大匹配的匹配边的数目 完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配. 最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择 最大独立数:选取最多的点,使任意所选两点均不相连 最小路径覆盖数:对于一个 DAG

算法大讲堂之二分图

二分图大讲堂——彻底搞定最大匹配数(最小覆盖数).最大独立数.最小路径覆盖.带权最优匹配 文本内容框架: §1图论点.边集和二分图的相关概念和性质 §2二分图最大匹配求解 匈牙利算法.Hopcroft-Karp算法 §3二分图最小覆盖集和最大独立集的构造 §4二分图最小路径覆盖求解 §5二分图带权最优匹配求解 Kuhn-Munkers算法 §6小结 每章节都详细地讲解了问题介绍,算法原理和分析,算法流程,算法实现四部分内容,力求彻底解决问题. §1图论点.边集和二分图的相关概念和性质 点覆盖.最

博弈论类题目小结——转载

出处http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 首先当然要献上一些非常好的学习资料: 基础博弈的小结:http://blog.csdn.net/acm_cxlove/article/details/7854530 经典翻硬币游戏小结:http://blog.csdn.net/acm_cxlove/article/details/7854534 经典的删边游戏小结:http://blog.csdn.net/acm

使用Apache POI导出Excel小结--导出XLS格式文档

使用Apache POI导出Excel小结 关于使用Apache POI导出Excel我大概会分三篇文章去写 使用Apache POI导出Excel小结--导出XLS格式文档 使用Apache POI导出Excel小结--导出XLSX格式文档 使用Apache POI导出Excel--大数量导出 导出XLS格式文档 做企业应用项目难免会有数据导出到Excel的需求,最近在使用其,并对导出Excel封装成工具类开放出来供大家参考.关于Apache POI Excel基本的概念与操作我在这里就不啰嗦

bzoj4025 二分图

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4025 [题解] 考虑对时间分治,用可撤回的启发式合并并查集来维护连通性. 二分图的条件是没有奇环,用并查集判即可. 对于时间区间[l,r],如果边在这期间都存在,那么就加入并查集,对于剩下的边分类,并且分治下去做. 对于每条边,在log个区间表示出来了,需要进行判断,启发式合并的getf是log的,所以复杂度为O(nlog^2n) # include <stdio.h> # includ

【转载】小结一下linux 2.6内核的四种IO调度算法

在LINUX 2.6中,有四种关于IO的调度算法,下面综合小结一下: 1) NOOP NOOP算法的全写为No Operation.该算法实现了最最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作.之所以说“大致”,原因是NOOP在FIFO的基础上还做了相邻IO请求的合并,并不是完完全全按照先进先出的规则满足IO请求.NOOP假定I/O请求由驱动程序或者设备做了优化或者重排了顺序(就像一个智能控制器完成的工作那样).在有些SAN环境下,这个选择可能是最好选择.Noop 对于 IO