题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1281
题解:
利用二分图匹配求最大匹配,将棋盘化成一个二分图,横纵坐标分别为左右两团,如果匹配到就说明该点可以放车。然后穷举所有最大匹配找到的边,观察删去该边后最大匹配值是否改变,改变的话即是重要点。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int V[505][505];
int check[1000];
int matching[1000];
int matching2[1000];
int n,m;
bool dfs(int u)
{
int i;
for(i = 1; i <= m; i++)
{
if(check[i] || V[u][i] != 1)
continue;
check[i] = 1;
if(matching[i] == -1 || dfs(matching[i]))
{
matching[i] = u;
return true;
}
}
return false;
}
bool dfs2(int u)
{
int i;
for(i = 1; i <= m; i++)
{
if(check[i] || V[u][i] != 1)
continue;
check[i] = 1;
if(matching2[i] == -1 || dfs(matching2[i]))
{
matching2[i] = u;
return true;
}
}
return false;
}
int solve(int n_left)
{
int i, ans = 0;
memset(matching, -1 ,sizeof(matching));
for(i = 1; i <= n_left; i++)
{
memset(check, 0 ,sizeof(check));
if(dfs(i))
ans++;
}
return ans;
}
int solve2(int n_left)
{
int i, ans = 0;
memset(matching2, -1 ,sizeof(matching2));
for(i = 1; i <= n_left; i++)
{
memset(check, 0 ,sizeof(check));
if(dfs2(i))
ans++;
}
return ans;
}
int main()
{
int k,cnt = 1 ;
while(cin >> n >> m >> k)
{
int an1, an2 = 0;
memset(V,0,sizeof(V));
while(k--)
{
int a,b;
cin >> a >> b;
V[a][b] = 1;
}
an1 = solve(n);
for(int i = 1; i <= an1; i ++)
{
if(matching[i] > 0)
{
V[matching[i]][i] = 0;
int abc = solve2(n);
V[matching[i]][i] = 1;//删去后一定要再加回去
if(abc < an1)
{
an2++;
}
}
}
cout << "Board "<< cnt ++<<" have "<< an2<<" important blanks for "<< an1 << " chessmen."<<endl;
}
return 0;
}