SSL-ZYC P2624 (洛谷P3355)【24题】骑士共存问题

题目大意:
这里写图片描述


思路:
这题是真的烦。。。
n<=200的数据很容易让我们想到匈牙利算法,所以就打了一发匈牙利。
然后T了。。。
于是就开始优化。
优化了我2个小时。。。
这里写图片描述
这里写图片描述
这里写图片描述

正题:

匈牙利算法(二分图)
不难发现,上图黄色格子不能攻击到其他黄色黄色格子,红色格子不能攻击到其他红色格子。
那么就可以把图以奇偶拆分,形成二分图。
然后枚举每个点一级他可以攻击到的点(增广路)。
最后用总格子数最大匹配=最多能放的骑士个数。


代码:

#include<cstdio>
#include<cstring>
using namespace std;

const short dx[8]={1,1,-1,-1,2,2,-2,-2};
const short dy[8]={-2,2,-2,2,-1,1,-1,1};  //八个方向
int n,m,sum,eve,odd,g[202][202],link[20002],a[20002][3],xx,yy,f;
bool vi[20002],ok[202][202];
char ch;

int read()  //输入流
{
    f=0;
    while(ch=getchar(),ch<=47||ch>=58);f=(f<<3)+(f<<1)+ch-48;
    while(ch=getchar(),ch>=48&&ch<=57) f=(f<<3)+(f<<1)+ch-48;
    return f;
}

bool find(int x)  //匈牙利
{
    int d=0,p=0;
    for (int i=0;i<8;i++)
    {
        xx=a[x][1]+dx[i];
        yy=a[x][2]+dy[i]; 
        if(xx<1||xx>n||yy<1||yy>n) continue;  //出界
        if(ok[xx][yy]||vi[g[xx][yy]]) continue;  //已经走过
        d=g[xx][yy];  //记录
        p=link[d];
        link[d]=x;
        vi[d]=true;
        if(find(p)||!p) return true;  //继续找增广路
        link[d]=p;
    }
    return false;
}

int main()
{
    n=read();
    m=read();
    for (int i=1;i<=m;i++)
      ok[read()][read()]=true;
    for (int i=1;i<=n;i++)
     for (int j=1;j<=n;j++)
      if(!ok[i][j])
      {
          if(!((i+j)&1))
           g[i][j]=++odd;  //奇数
          else
          {
              g[i][j]=++eve;  //偶数
              a[eve][1]=i;
              a[eve][2]=j;
          }
      }
    sum=n*n-m;
    for (int i=1;i<=eve;i++)  //枚举偶数点
    {
        memset(vi,0,sizeof(vi));
        if(find(i)) sum--;  //有一种方法就减1
    }
    printf("%d\n",sum);
    return 0;
}
posted @ 2018-06-22 11:09  全OI最菜  阅读(121)  评论(0编辑  收藏  举报