题目描述
![](http://cqoi.net:2012/images/oj/1385.jpg)
输入
输出
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
3 2
1 1
3 3
样例输出
5
由于没有绑定手机而苦逼了很久,所以为了补偿就来一篇吧。
二分图呢,就是这样一种图:可以把所有节点分成两个部分,x与y,而所有边必须是x连接到y,不允许x或y中出现自己连接的情况。而由于二分图的这种性质也是定义,就出现了一种问题——二分图的最大匹配。就是求二分图中选出尽可能多的边,让任意两条边不相连。有一种通俗的诠释:一群剩男剩女进行组成伴侣,在不同性恋、不一夫多妻、一妻多夫的情况下撮合尽可能多的情侣。
匈牙利算法就可以解决这种问题。具体操作是这样的:
1.寻找一个没有匹配的x节点(剩男),然后从它所有有边相连的(互相有感觉的)y节点中寻找一个同样没有匹配的(剩女),这时,将他们加入到匹配里面(撮合成一对),任务完成(撮合成功),再进行一遍1,直到无法进行1为止(没有剩男了~)。如果有边相连的y全部都在匹配中(没有两厢情愿的女孩子是单身),则对每一个相连的y进行2
2.将这个有边相连的(有男朋友的)y的x(男朋友)进行待定,如果x可以找到除了自己的其他空闲的y(有其他剩女可以选),那么将匹配关系变一变(小三上位,原配另找新欢),就多了一对匹配不是么?但如果同样没有找到怎么办?一样对所有相连的y进行2操作,直到多了一对匹配为止~如果找遍所有人都无法解决问题,很抱歉原来的这位x,你永远没有办法匹配(一辈子单身,惨)了……
咳咳咳,通过黑色和白色字的讲解,可以大致了解匈牙利算法了。那么来看看这道题吧。
我们可以显而易见的看出,按国际象棋格子的分法,有黑色和白色之分,可以很容易的发现,马从黑色只能跳到白色,白色只能跳到黑色,也就是处于白色格子的没有自区域的相连,黑的同样没有,符合二分图的性质,而一步可以跳到的地方就相当于有一条边相连。
有边相连的格点不可以同时放马,所以放了一个马与之相连的所有边就全部被控制。删去最少点使得所有边都不会冲突,这就是最小点覆盖边问题!答案是等于最大匹配的!然后用总数减去最大匹配数即可了!
当然,有了思路,要想不超时,就看你的代码丑不丑了……
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int getint()
{
int p=0,f=0;
char c=getchar();
while((c<'0'||c>'9')&&c!='-')c=getchar();
if(c=='-')
f=1,c=getchar();
while(c>='0'&&c<='9')
p=p*10+c-'0',c=getchar();
return f?-p:p;
}
void putint(int p)
{
if(p<0)putchar('-'),p=-p;
if(p>9)putint(p/10);
putchar(p%10+'0');
}
int n,m,sum,cnt[2];
bool A[205][205];
int num[205][205];
int d[8][2]={{2,1},{-2,1},{2,-1},{-2,-1},{1,2},{-1,2},{1,-2},{-1,-2}};
int to[20005][10];
int CP[20005];
bool vis[20005];
bool check(int x,int y)
{
if(x<1||y<1||x>n||y>n||A[x][y]||vis[num[x][y]])return 0;
return 1;
}
bool xyl(int x)
{
for(int i=1;i<=to[x][0];i++)
{
int p=to[x][i];
if(!vis[p])
{
vis[p]=1;
if(!CP[p]||xyl(CP[p]))
{
CP[p]=x;
return 1;
}
}
}
return 0;
}
int main()
{
n=getint();m=getint();
for(int i=1;i<=m;i++)
{
int a=getint(),b=getint();
A[a][b]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!A[i][j])
num[i][j]=++cnt[(i+j)%2];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!A[i][j]&&(i+j)%2==0)
for(int k=0;k<8;k++)
if(check(i+d[k][0],j+d[k][1]))
to[num[i][j]][++to[num[i][j]][0]]=num[i+d[k][0]][j+d[k][1]];
for(int i=1;i<=cnt[0];i++)
{
sum+=xyl(i);
memset(vis,0,sizeof(vis));
}
putint(cnt[1]+cnt[0]-sum);
}