大佬博客:http://blog.csdn.net/u013480600/article/details/38686499
HDU 4185 Oil Skimming(二分图最大匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=4185
题意:
有个N*N的字符矩阵,你必须用竖直或水平的1*2小矩阵去覆盖字符矩阵中相邻的两个”#”字符. 且你用的1*2小矩阵不能重叠且只能覆盖”#”字符. 问你最多能用多少个1*2的小矩阵?
分析:
把原字符矩阵的所有”#”都编号看成一个一个的节点.如果有两个”#”相邻,那么就在它们之间连接一条边. 那么我们就得到了一个二分图(该图必定二分,因为矩阵就是二分图 且 行号+列号==奇数的点与行号+列数号==偶数的点 分别属于不同的两个点集).
现在的问题是我们要求这个二分图的最大匹配边数,找出尽量多的匹配边. 那么这些匹配边就是一个个不重叠覆盖了”#”字符的1*2矩阵.
将原图的所有”#”字符所在的格子分成左右两个点集算二分图可以,不过需要(用行号加列号的奇偶性)将格子分为左右两边,有点麻烦. 这里我们直接将原图翻倍,求出翻倍图的匹配数/2 即是答案.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 610;
int mp[maxn][maxn];
struct Max_Match
{
int n;
vector<int> g[maxn];
bool vis[maxn];
int left[maxn];
void init(int n)
{
this -> n = n;
for(int i = 0; i <= n; i++)
g[i].clear();
memset(left, -1, sizeof(left));
}
bool match(int u)
{
for(int i = 0; i < g[u].size(); i++)
{
int v = g[u][i];
if(!vis[v])
{
vis[v] = true;
if(left[v] == -1 || match(left[v]))
{
left[v] = u;
return true;
}
}
}
return false;
}
int solve()
{
int ans = 0;
for(int i = 1; i <= n; i++)
{
memset(vis, false, sizeof(vis));
if(match(i))
ans++;
}
return ans;
}
}MM;
int main()
{
int T;scanf("%d", &T);
for(int kase = 1; kase <= T; kase++)
{
int N;
int n = 0;
scanf("%d", &N);
memset(mp, 0, sizeof(mp));
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
{
char ch;
scanf(" %c", &ch);
if(ch == '#')
mp[i][j] = ++n;
else mp[i][j] = 0;
}
MM.init(n);
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
if(mp[i][j] > 0)
{
if(mp[i+1][j] > 0) MM.g[mp[i][j]].push_back(mp[i+1][j]);
if(mp[i-1][j] > 0) MM.g[mp[i][j]].push_back(mp[i-1][j]);
if(mp[i][j+1] > 0) MM.g[mp[i][j]].push_back(mp[i][j+1]);
if(mp[i][j-1] > 0) MM.g[mp[i][j]].push_back(mp[i][j-1]);
}
printf("Case %d: %d\n", kase, MM.solve()/2);
}
return 0;
}