SSL-1344 Knights【最大独立集】

大意

在一个 n×n n × n 的棋盘上有 m m 个点被扣掉了,现在问你最多可以放几匹马。

数据范围

n<=200,m<=n n <= 200 , m <= n

思路

匈牙利算法
我们知道
定理: U=VM U = V − M V V =顶点数, M M =最大匹配数(最小覆盖数), U U =最大独立集。
所以,我们给每个点都标号,但并不是按 1..nn 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);//最大独立集数
}

你可能感兴趣的:(GT,图的匹配问题)