题意

有一个 n*n 的图,. 代表空白区域,X 代表墙,现在要在空白区域放置结点,要求同一行同一列只能放一个,除非有墙阻隔,问最多能放多少个点

思路

只有在墙的阻隔情况下,才会出现一行/列出现多个点的情况,那么可以考虑进行缩点,将同一行且没有墙体阻隔的区域缩成一个点,放到左点集中,将同一列且没有墙体阻隔的区域缩成一个点,放到右点集中,从而建成一个二分图

 Fire Net(HDU-1045)(匈牙利最大匹配)(建图方式)

 

假设 i 为行编号,j 为列编号,若 i-j 之间存在一条边,就相当于在方格 (i,j) 上放了一个点,这个假设使得在没有墙体阻隔的情况下,i 行 j 列不能再放其他的点,那么在不考虑 不能同行同列 的情况下,将所有边连接起来,即行列缩点后,对应方格编号连边 

建好图后,在图上求最大匹配即可

Fire Net(HDU-1045)(匈牙利最大匹配)(建图方式)

 

C++代码一

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-9
#define INF 0x3f3f3f3f
#define LL long long
const int MOD=1E9+7;
const int N=1000+5;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
using namespace std;
int n;//n行n列
bool vis[N];//vis[i]表示是否在交替路中
int link[N];//存储连接点
int G[N][N];//存边
char str[N][N];
int x[N][N],cntX;//行点集
int y[N][N],cntY;//列点集
bool dfs(int x){
    for(int y=0;y<cntY;y++){//对x的每个邻接点
        if(G[x][y]==1&&!vis[y]){//不在交替路中
            vis[y]=true;//放入交替路
            if(link[y]==-1 || dfs(link[y])){//如果是未匹配点,说明交替路是增广路
                link[y]=x;//交换路径
                return true;//返回成功
            }
        }
    }
    return false;//不存在增广路,返回失败
}
int hungarian(){
    int ans=0;//记录最大匹配数
    memset(link,-1,sizeof(link));
    for(int i=0;i<cntX;i++){//从左侧开始每个结点找一次增广路
        memset(vis,false,sizeof(vis));
        if(dfs(i))//找到一条增广路,形成一个新匹配
            ans++;
    }
    return ans;
}
int main(){
 
    while(scanf("%d",&n)!=EOF&&n){
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        memset(G,false,sizeof(G));
 
        for(int i=0;i<n;i++)
            scanf("%s",str[i]);
 
        //对行缩点
        cntX=1;
        for(int i=0;i<n;i++){//第i行
            for(int j=0;j<n;j++){//第j列
                if(str[i][j]=='.')//同一区域
                    x[i][j]=cntX;
                if(str[i][j]=='X')//墙体阻隔
                    cntX++;
            }
            cntX++;//下一行
        }
 
        //对列缩点
        cntY=1;
        for(int j=0;j<n;j++){//第j列
            for(int i=0;i<n;i++){//第i行
                if(str[i][j]=='.')//同一区域
                    y[i][j]=cntY;
                if(str[i][j]=='X')//墙体阻隔
                    cntY++;
            }
            cntY++;//下一列
        }
 
        //连边
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(str[i][j]=='.')
                    G[x[i][j]][y[i][j]]=true;
 
        printf("%d\n",hungarian());
    }
    return 0;
}
View Code

相关文章: