hdu 5093 Battle ships (二分图)

二分图最大匹配问题

遇到冰山就把行列拆成两个部分。每个部分x也好,y也好只能匹配一次

图画得比较草,将就着看

 hdu 5093 Battle ships (二分图)

横着扫一遍,竖着扫一遍,得到编号

一个位置就对应一个(xi,yi)就是X集到Y集的一条边,

由题意,每个点只能被选择一次。所以最大匹配的边数就是答案了。

算法过程

当增广路不存在的时候,就是二分图最大匹配。(同样适用其他任意图,可以求最大流)

通常都是先贪心求一个匹配,然后开始增广。

寻找增广路的过程:

  一个没有和任意边匹配的点叫做未盖点,从左集X中一个未盖点u出发寻找增广路。

  从u出发,选一个非匹配边到达Y集中的v,如果v没匹配,那么就找到一条增广路(只要把之前走过的匹配边和非匹配边交换,匹配边数加一)。否则沿着v的匹配边回来,然后重复以上过程。

做这题的过程中,把数组开小了导致TLE...还加了个反向边,不过在debug过程中到是发现了不用每次都memset vis数组的小技巧

#include<cstdio>
#include<cstring>

const int maxn = 52;
const int maxv = 2550; // 500
const int maxe = (maxv*maxv);
char pg[maxn][maxn];
int g[maxn][maxn][2];

int to[maxe],nxt[maxe],head[maxv],ecnt;
int match[maxv];


void addEdge(int u,int v)
{
    to[ecnt] = v;
    nxt[ecnt] = head[u];
    head[u] = ecnt++;
}

int vis[maxv];
int times;
//find augmenting path
bool dfs(int u)
{
    for(int i = head[u]; ~i; i = nxt[i]){
        int v = to[i];
        if(vis[v]!= times){
            vis[v]  = times;
            if(!~match[v] || dfs(match[v]) ){
                match[v] = u;
                return true;
            }
        }

    }
    return false;
}

int x_num,y_num;

void go()
{
    memset(match,-1,sizeof(match));
    int ans = 0;
    for(int i = 0; i < x_num; i++ ){
        times++;
        if(dfs(i)) ans++;
    }
    printf("%d
",ans);
}

void init(){
    memset(head,-1,sizeof(head));
    ecnt = 0;
    x_num = 0;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    times = 0;
    scanf("%d",&T);
    while(T--){
        init();
        int m,n;
        scanf("%d%d",&m,&n);
        for(int i = 0; i < m; i++)
            scanf("%s",pg[i]);
        bool flag;//如果某行没有找到那么编号不应该增加
        for(int i = 0; i < m; i++){
            flag = false;
            for(int j = 0; j < n; j++){
                if(pg[i][j] == 'o') g[i][j][0] = -1;
                else if(pg[i][j] == '#') g[i][j][0] = -1,x_num++;
                else g[i][j][0] =  x_num,flag = true;
            }
            if(flag) x_num++;
        }

        y_num = x_num;
        for(int j = 0; j < n; j++){
            flag = false;
            for(int i = 0; i < m; i++){
                 if(pg[i][j] == 'o') ;
                else if(pg[i][j] == '#') y_num++;
                else g[i][j][1] =  y_num,flag = true;
            }
            if(flag) y_num++;
        }

        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++){
                if(~g[i][j][0]){
                    addEdge(g[i][j][0],g[i][j][1]);
                }
            }
        go();
    }
    return 0;
}