HDU-4185 Oil Skimming(二分匹配,匈牙利算法)

题意:给出一个N*N图,#表示有油,.表示水。现在要统计最多有多少块符合条件的油田
条件:油田不能重合,为1 x 2 的矩形,可以竖着1x2也可以横着1x2。
思路:我们可以把所有的油田看作一个单独的块,匹配时就搜索当前块上下左右相邻是否有油田
实际上就是 油田作为一个点,两两相邻的点符合条件就连接一条边。然后用二分图的匹配形式去找到最大匹配
同时这种最大匹配实际上有重复(连成无相图了),所以最后还要除以2

完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxm = 1e4;
const int maxn = 1005;
char G[maxn][maxn];
int bmap[maxn][maxn];
int cur[maxn][maxn];
int n;
int cnt ; 
int match[maxn];//表示匹配关系(左集合到右集合的匹配)
int vis[maxn];//表示是否被访问过
//记录匹配个数
bool Find(int u){
       for(int i = 0;i<cnt;i++){
           int v = i;
           if(!vis[v]&&bmap[u][v]){
            vis[v] = 1;
            if(!match[v]||Find(match[v])){
                match[v] = u;
                return true;
            }
        }
    }   
    return false;
}
int hungary(){
    int count = 0;
    memset(match,0,sizeof(match));
    for(int i =0;i<cnt;i++){
        memset(vis,0,sizeof(vis));
        if(Find(i)) count++;
    }
    return count;
}

int main(){
    int T;
    cin>>T;
    int cas = 0;
    while(T--){
        memset(G,0,sizeof(G));
        cnt = 0;
        cin>>n;
        for(int i =0;i<n;i++){
            cin>>G[i];
        }
        for(int i =0 ;i<n;i++){
            for(int j =0;j<n;j++){
                if(G[i][j]=='#') cur[i][j] = cnt++;
            }
        }
        memset(bmap,0,sizeof(bmap));
        for(int i =0;i<n;i++){
            for(int j = 0;j<n;j++){
                if(G[i][j]!='#') continue;
                if(i>0&&G[i-1][j]=='#') bmap[cur[i][j]][cur[i-1][j]] = 1;
                if(i<n-1&&G[i+1][j]=='#') bmap[cur[i][j]][cur[i+1][j]] = 1;
                if(j>0&&G[i][j-1]=='#') bmap[cur[i][j]][cur[i][j-1]] = 1;
                if(j<n-1&&G[i][j+1]=='#') bmap[cur[i][j]][cur[i][j+1]] = 1;
            }
        }
        int num = hungary();
        cout<<"Case "<<++cas<<": "<<num/2<<endl;
    }    
}

 

posted @ 2019-08-12 15:23  Tianwell  阅读(148)  评论(0编辑  收藏  举报