在一个n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示。棋盘
上某些方格设置了障碍,骑士不得进入。
对于给定的n*n个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑
士,使得它们彼此互不攻击。
输入描述 Input Description
第一行有2 个正整数n 和m (1<=n<=200, 0<=m<n^2),
分别表示棋盘的大小和障碍数。接下来的m 行给出障碍的位置。每行2 个正整数,表示障
碍的方格坐标。
输出描述 Output Description
将计算出的共存骑士数输出
样例输入 Sample Input
3 2
1 1
3 3
样例输出 Sample Output
5
图中 : S能攻击到8个X的地方;
把棋盘染色,相邻的染不同色,因为对角线是攻击不到的,不是对角线的两个点,是有可能互相攻击到的, .;
这样就能够把棋盘看成二分图,相邻的在不同集合,颜色一样的在一个集合 . 如 (i+j)%2==0 在一个集合, (i+j)%2!=0在一个集合 ; 然后就转换成求图的最大独立集(集合里任易两点不连边) ; 二分图 最大独立集= 顶点数(出去障碍) - 最大匹配数 ; 但是这题数据大, 不能用匈牙利算法求最大匹配 ; 所以要转换成 网络最大流求最大匹配 ;
建图 : 加一个源点S , 让S与X集合的点连边 ,容量设为1 ;
加一个汇点T ,让Y集合的点连边到T ,容量也为1 ;
X集合中的点,连接能攻击到的Y集合的中的点, 连接边 xi-->yi ;容量也为1 ;
最后用dinic算法求网络最大流, 就是最后的匹配数 ;
第一次搞网络流 !!!!!!!!!! 终于破网络流的处了 ...
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
#define INF 99999999
int h[8]={-1,-2,-2,-1,1,2,2,1};
int l[8]={-2,-1,1,2,2,1,-1,-2};
bool can[201][201];
int maxflow=0; //网络最大流
const int MAXN=200*200+2;
struct node
{
int x,y,c; //存边,c是边的可增容量,即在这条边能修改的流量.
int op; //op是反向边.即残留网络中的,反向边.
int next;
}p[MAXN*8*2+1];
int first[MAXN];
int level[MAXN],st[MAXN],t[MAXN],state[MAXN];
int count_=0;
void addedge(int a,int b,int c) //加边,动态链表存储
{
count_++;
p[count_].x=a,p[count_].y=b,p[count_].c=c;
p[count_].next=first[a]; //指向上次一条a-->x,的边的结点
p[count_].op=count_+1;first[a]=count_; //op指向反向边 ,所以count+1 ;
count_++;
p[count_].x=b,p[count_].y=a,p[count_].c=0; //初始化反向边为0 ;因为刚开始还没流量
p[count_].next=first[b];
p[count_].op=count_-1;first[b]=count_; //op指向正向边,所以count-1 ;//a-->b ,和 b -->a ,在的结构体中的位置 通过op互相指向 .
}
int N,M;
int S,T;
bool make_level() / /建层次网络
{
int head,tail,i,j;
memset(level,-1,sizeof(level));
level[S]=0; //初始化
st[head=tail=0]=S;
while(head<=tail)
{
i=st[head++];
for (int v=first[i];v!=-1;v=p[v].next) //从i点流出去,可能有不止一条流
{
j=p[v].y;
if (p[v].c&&level[j]==-1)
{
level[j]=level[i]+1; //赋值
if (j==T) return true; //层次网络构建完了
st[++tail]=j;
}
}
}
return false;
}
void dinic() / / 寻找增光路,并修改流量.
{
int i,j,delta,Stop;
memset(st,-1,sizeof(st)); //可有可无.因为下面会覆盖掉以前的值
st[Stop=1]=S; //初始化加入源点, 注意st[]是从st[1]开始的;
for (int i=S;i<=T;i++) //赋值 ; 可有可无
{
t[i]=first[i];
}
while(Stop)
{
i=st[Stop];
if (i!=T) //还没到汇点
{
while(t[i]!=-1)
{ //从i-->j,找到一条边,这条在层次网络中 ;
if (p[t[i]].c&&level[i]+1==level[j=p[t[i]].y]) break;
t[i]=p[t[i]].next; //因为从i出去的边有很多,知道找到边,是在层次网络中的.
}
if (t[i]!=-1) //找到了
{
st[++Stop]=j; //加入;
state[Stop]=t[i]; //存这条边在的结构体的位置; 注意state[]是从2开始的;
}
else //一条都找不到,回溯,继续找
{
Stop--;
level[i]=-1;
}
}
else //如果到汇点了,说明找到了一条增光路
{
delta=INF;
for (i=Stop;i>=2;i--) //在这条增光路中找到可改变流量最小的,即c
if (p[state[i]].c<delta) delta=p[state[i]].c;
maxflow+=delta; //得到一个修改流量
for (i=Stop;i>=2;i--) //调整层次网络
{
p[state[i]].c-=delta; //可修改流量减少了delta
int op_=p[state[i]].op; //反向边
p[op_].c+=delta; //相应的,反向流量就增加了c
if (p[state[i]].c==0) Stop=i-1; //如果修改完后,这条边已经没有流量可修改了,
} //就删除这条边.退一步(Stop=i-1) ,继续下次找增光路;
}
}
}
int main()
{
memset(can,true,sizeof(can));
memset(first,-1,sizeof(first));
cin>>N>>M;
for (int i=1;i<=M;i++) //存障碍
{
int x,y;
cin>>x>>y;
can[x][y]=false;
}
S=0,T=N*N+1; //初始化源点,汇点
for (int i=1;i<=N;i++)
{
for (int j=1;j<=N;j++)
{
if (!can[i][j]) continue;if ((i+j)%2==0) {addedge(((i-1)*N+j),T,1);continue;} //y集合中的点,跟汇点连
int id1=((i-1)*N+j); //(i-1)*N+j)是这个坐标,是第几个点;
addedge(0,id1,1); //X集合中的点,跟源点连
for (int k=0;k<8;k++)
{
int x,y;
x=i+h[k],y=j+l[k]; //攻击到y集合中的点
if (x<1||y<1||x>N||y>N) continue;
if (!can[x][y]) continue;
int id2=((x-1)*N+y);
addedge(id1,id2,1); //X集合中的点和y集合中点,连边
}
}
}
while(make_level()) //建层次网络,找一次,修改, 直到无法建层次网络了 ;
dinic();
cout<<N*N-M-maxflow<<endl; //格子数-最大匹配数 ;
getchar();
getchar();
return 0;
}