题目:hdoj1045
题意:给出一个图,其中有 . 和 X 两种,. 为通路,X表示墙,在其中放炸弹,然后炸弹不能穿过墙,问你最多在图中可以放多少个炸弹?
分析:这道题目是在上海邀请赛的题目的数据简化版,数据水了,所以有很多方法,这里讲二分图最大匹配,题目难点在于建图
想到用暴力过,但是事实证明我想多了。然后又想到多重二分匹配,后来发现没有办法表示图中的行列中墙的阻隔,后来看了别人的建图,瞬间觉得高大上。
建图,首先把每一行中的可以放一个炸弹的一块区域标记为同一个数字,数字不重复,然后列做相同的处理,即缩点!
缩点之后原图矩阵中每个点都对用一个行数字和一个列数字,然后按照这两个数字进行二分匹配,其相同值只取一个,得到的结果就是ans;
注意:每次判断增广的时候首先检查一下当前点有没有匹配,如果匹配就不用搜索,因为有多个值对应一个点,所以...
代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10;
#define Del(x,y) memset(x,y,sizeof(x))
char map[N][N];
int path[N][N];
int line[N][N],row[N][N],link[N],vis[N],vlink[N];
int n,cnt_row,cnt_line;
bool dfs(int x)
{
for(int i=0;i<cnt_line;i++)
{
if(path[x][i]==1 && vis[i]==0)
{
vis[i]=1;
if(link[i]==-1 || dfs(link[i]))
{
link[i]=x;
vlink[x]=i;
return true;
}
}
}
return false;
}
void solve()
{
int ans=0;
Del(link,-1);
Del(vlink,-1);
for(int i=0;i<cnt_row;i++)
{
if(vlink[i]==-1){ ///注意!标记找过的
Del(vis,0);
if(dfs(i))
ans++;
}
}
printf("%d\n",ans);
}
int main()
{
//freopen("Input.txt","r",stdin);
while(~scanf("%d",&n) && n)
{
char c;
Del(map,0);
for(int i=0;i<n;i++)
{
getchar();
for(int j=0;j<n;j++)
scanf("%c",&map[i][j]);
}
Del(line,-1);
Del(row,-1);
cnt_row=0,cnt_line=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(map[i][j] == '.' && row[i][j] == -1)
{
for(int k = j; map[i][k] == '.' && k < n; ++k)
row[i][k] = cnt_row;
cnt_row++;
}
if(map[j][i] == '.' && line[j][i] == -1)
{
for(int k = j; map[k][i] == '.' && k < n; ++k)
line[k][i] = cnt_line;
cnt_line++;
}
}
}
Del(path,0);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(map[i][j]=='.')
path[row[i][j]][line[i][j]]=1;
}
}
solve();
}
return 0;
}