G - 棋盘游戏 (二分匹配)
先考虑在棋盘上尽可能的放棋子,使得任意棋子不在同一行同一列,将棋盘的行看做左边的点集,棋盘的列看做右边的点集,若某个格子(i,j)可行,那么就从左 i 连到 右 j,这个二分图的最大匹配即为这个棋盘能放的最多棋子数。
现要找出有二分图中有多少关键边,很明显关键边要在算出来的匹配中找,因此只需将棋盘点对应的边删除再求一次最大匹配,看匹配数是否减小,若减小了,则说明这个边也即棋盘的点是关键的,输出即可。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 105
int m,n;
bool vis[N];
bool g[N][N];
int link[N];
int dfs(int x)
{
for(int i=1; i<=m; i++)
{
if(!vis[i]&&g[x][i])
{
vis[i]=true;
if(link[i]==-1||dfs(link[i]))
{
link[i]=x;
return true;
}
}
}
return false;
}
int mac()
{
int ans=0;
for(int i=1; i<=n; i++)
{
memset(vis,false,sizeof(vis));
if(dfs(i))
ans++;
}
return ans;
}
int main()
{
int k,x,y,cas=1;
while(~scanf("%d%d%d",&n,&m,&k)&&(n+m+k))
{
memset(g,false,sizeof(g));
memset(link,-1,sizeof(link));
while(k--)
{
scanf("%d%d",&x,&y);
g[x][y]=true;
}
int maxx=mac(),cnt=0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
//printf("%d\n",g[i][3]);
if(g[i][j])
{
g[i][j]=false;
memset(link,-1,sizeof(link));
int c=mac();//printf("%d\n",c);
if(c<maxx)
cnt++;
g[i][j]=true;
}
}
}
printf("Board %d have %d important blanks for %d chessmen.\n",cas++,cnt,maxx);
}
return 0;
}