在一个 n×n n × n 的棋盘上有 m m 个点被扣掉了,现在问你最多可以放几匹马。
n<=200,m<=n n <= 200 , m <= n
匈牙利算法
我们知道
定理: U=V−M U = V − M ; V V =顶点数, M M =最大匹配数(最小覆盖数), U U =最大独立集。
所以,我们给每个点都标号,但并不是按 1..n∗n 1.. n ∗ n 去标号,而是奇数偶数分别来标号,这样子可以降低一半的时间复杂度。
在找增广路的时候,不能一到 n n 去扫描,可以枚举八个方向,当然也可用邻接表,时间复杂度自然是优于前者,因为不是每个点都可以走到八个方向
#include
#include
#define N 202
#define M 20002
#define LL long long
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;int n,m,ans,one,g[N][N],two,link[M],xq[M][2];
bool vis[M],ok[N][N];
const short dx[8]={1,1,-1,-1,2,2,-2,-2};
const short dy[8]={-2,2,-2,2,-1,1,-1,1};//八个方向不解释
LL read()//输入流别解释
{
char c;int f=0,d=1;
while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48;
return d*f;
}
bool check(int x,int y)
{
if(x<1||x>n||y<1||y>n) return false;//越出棋盘显然不行
if(ok[x][y]||vis[g[x][y]]) return false;//已经被扣掉或者匹配显然不行
return true;//否则就可以了
}
bool find(int x)//求匹配
{
int k=0,q=0,x1,y1;
r(i,0,7)//这里用邻接表可以使时间降低2000ms
{
x1=xq[x][0]+dx[i];
y1=xq[x][1]+dy[i];
if(check(x1,y1))//判断
{
k=g[x1][y1];
q=link[k];
link[k]=x;
vis[k]=true;
if(!q||find(q)) return true;
link[k]=q;
}
}
return false;
}
int main()
{
n=read();m=read();
r(i,1,m) ok[read()][read()]=true;
r(i,1,n)
r(j,1,n)
if(!ok[i][j])
{
if((i+j)&1)
g[i][j]=++one;//奇数点个数
else
{
g[i][j]=++two;//偶数点个数
xq[two][0]=i;
xq[two][1]=j;
}
}
ans=n*n-m;//顶点数
r(i,1,two)
{
memset(vis,0,sizeof(vis));
if(find(i)) ans--;//最大匹配数
}
printf("%d",ans);//最大独立集数
}