hdu 1045 Fire Net(二分图)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1045
题目大意为给定一个最大为4*4的棋盘,棋盘可以放置堡垒,处在同一行或者同一列的堡垒可以相互攻击,在棋盘上也有城墙,可以隔离同一行同一列堡垒的相互攻击,“X”表示有城墙,“ . ”表示空地,问该棋盘最多可以放置多少个堡垒?
此题为二分图匹配问题,但是起初我不知道怎么建图,看题解发现一个规律,同一行同一列的连续空地不能放置两个堡垒,那么同一行同一列的连续空地的交点放置一个堡垒后,该行该列将不能再放置堡垒,这样很容易联想到二分图的性质,那么把连续“行”空地设为l集合,连续的“列”空地设为r集合,也就是说把连续行空地和连续列空地缩点,如果两者在原图中有交点,则可以建边,这样便可以轻易建图了,建图之后跑一下匈牙利算法即可得出答案,其二分图最大匹配数则为该棋盘最多可以放置的堡垒。
AC代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1001;
int visit[maxn];//访问数组
bool g[maxn][maxn];//建立二分图
int match[maxn];//匹配数组
int l,r;//l和r两集合
bool dfs(int u){
// i为建图后r集合的节点 ,u为l集合的节点
for(int i = 1;i<=r;i++){//依次遍历集合r
if(g[u][i] && !visit[i]){//如何u和i之间有边,而且i没有被访问过
visit[i] = true;//标记
if(match[i] == -1 || dfs(match[i])){//如果该i没有匹配过则直接匹配u,
//如果匹配过那么尝试dfs寻找增广路
match[i] = u;
return true;
}
}
}
return false;
}
int hungary(){
int ans = 0;
memset(match,-1,sizeof(match));//初始化r匹配的 l
for(int i = 1;i<=l;i++){
memset(visit,false,sizeof(visit));//初始化l的visit数组,表示都没有访问过
if(dfs(i)){//跑dfs遍历
ans++;//找到一个匹配
}
}
return ans;
}
int main(){
int n;
vector<int> res;
while(~scanf("%d",&n))
{
memset(g,false,sizeof(g));//初始化图
int markl[5][5];
vector<string> s;
if(n==0){
break;
}
for(int i = 0;i<n;i++){
string t;
cin>>t;
s.push_back(t); //存string数据
}
int step = 0;//step标记点的序号
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
//如下是缩点操作
if(s[i][j] == '.'){
if(j == 0){
step++;
markl[i][j] = step;
continue;
}
if(s[i][j-1] == 'X'){
step++;
}
markl[i][j] = step;
}
}
}
l = step;//记录集合l的数量
int markr[5][5];
step = 0;
for(int j = 0;j<n;j++){
for(int i = 0;i<n;i++){
if(s[i][j] == '.'){
if(i == 0){
step++;
markr[i][j] = step;
continue;
}
if(s[i-1][j] == 'X'){
step++;
}
markr[i][j] = step;
}
}
}
r = step;
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
if(s[i][j] == '.')
g[markl[i][j]][markr[i][j]] = true;//如果缩点后两者有交点,则在邻接矩阵上建边
}
}
cout<<hungary()<<endl;
}
return 0;
}