cunzai_zsy0531

关注我

P3355 骑士共存问题 题解

Post time: 2020-03-03 19:33:17

题目链接

本题与方格取数问题很像,建议大家先做这道题。

与P2774相同的地方不再赘述,分二分图的模式一样(只要保证二分图两个独立点集当中不会有边即可)。只是要注意障碍点,直接用一个数组记录状态,如果此点有障碍直接跳过即可。

连边的时候,注意马攻击的格子,可以直接定义数组解决:

int dx[8][2]={{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2},{-2,1}};

还是两个点集分别连源点汇点(流量 \(1\)),然后把马可以互相攻击的格子相连(流量 \(INF\)),这样保证最小割不会割掉中间边。

最终的答案就是总点数(要注意障碍点不能算)-最大流。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int N=40000+5,INF=0x3f3f3f3f;
struct Edge{int u,v,w,nxt;}e[N*10];
struct Node{int u,val;bool operator <(const Node &a)const{return val<a.val;}};
priority_queue<Node>q;
int dx[8][2]={{-2,-1},{-1,-2},{1,-2},{2,-1},{2,1},{1,2},{-1,2},{-2,1}};
int h[N],d[N],flow[N],gap[N*2];//要注意gap[]数组一定要开到maxn两倍(因为层数会超n),这里我卡了一上午
bool vis[N],mapp[205][205];
int n,m,s,t,sum,tot=1;
inline int getnum(int x,int y){return (x-1)*n+y;}
inline bool check(int x,int y){return x>=1&&x<=n&&y>=1&&y<=n;}
inline void add(int u,int v,int w){//建双向边
	e[++tot]=(Edge){u,v,w,h[u]};h[u]=tot;
	e[++tot]=(Edge){v,u,0,h[v]};h[v]=tot;
}
bool bfs(){
	memset(d,-1,sizeof(d));
	queue<int>Q;
	Q.push(t);
	d[t]=0;
	while(!Q.empty()){
		int u=Q.front();
		Q.pop();
		for(int i=h[u];i;i=e[i].nxt){
			int v=e[i].v,w=e[i^1].w;
			if(d[v]==-1&&w){
				d[v]=d[u]+1;
				Q.push(v);
			}
		}
	}
	return d[s]!=-1;
}
void Init(){
	for(int i=h[s];i;i=e[i].nxt){
		int v=e[i].v,w=e[i].w;
		if(w){
			e[i].w=0,e[i^1].w=w;
			flow[s]-=w,flow[v]+=w;
			if(v!=s&&v!=t&&!vis[v]){
				vis[v]=1;
				q.push((Node){v,d[v]});
			}
		}
	}
}
void Push(int u){
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v,w=e[i].w;
		if(d[u]==d[v]+1&&w){
			int f=min(flow[u],w);
			e[i].w-=f,e[i^1].w+=f;
			flow[u]-=f,flow[v]+=f;
			if(v!=s&&v!=t&&!vis[v]){
				vis[v]=1;
				q.push((Node){v,d[v]});
			}
		}
	}
}
void Gap(int u){
	for(int i=1;i<=n*n;++i){
		if(i!=s&&i!=t&&d[i]>d[u]&&d[i]<=n*n) d[i]=n*n+1;
	}
} 
void Relabel(int u){
	d[u]=INF;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v,w=e[i].w;
		if(w) d[u]=min(d[u],d[v]);
	}
	++d[u];
}
int HLPP(){
	if(!bfs()) return 0;
	memset(gap,0,sizeof(gap));
	memset(flow,0,sizeof(flow));
	memset(vis,0,sizeof(vis));
	d[s]=n*n;
	for(int i=1;i<=n*n;++i){
		if(d[i]!=-1) gap[d[i]]++;
	}
	Init();
	while(!q.empty()){
		int u=q.top().u;
		q.pop();
		vis[u]=0;
		Push(u);
		if(flow[u]){
			gap[d[u]]--;
			if(!gap[d[u]]) Gap(u);
			Relabel(u);
			gap[d[u]]++;
			vis[u]=1;
			q.push((Node){u,d[u]});
		}
	}
	return flow[t];
} 
int main(){
	scanf("%d%d",&n,&m);s=n*n+1,t=n*n+2;
	for(int i=1,x,y;i<=m;++i){
		scanf("%d%d",&x,&y);
		mapp[x][y]=1;
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			if(mapp[i][j]) continue;//一定要注意障碍点不要算到总点数里!
			++sum;//总点数
			bool ok=0;
			int k=getnum(i,j);
			if((i+j)%2) add(s,k,1),ok=1;//连源点
			else add(k,t,1);//连汇点
			if(ok){
				for(int q=0;q<8;++q){
					int ex=i+dx[q][0],ey=j+dx[q][1];
					if(check(ex,ey)&&!mapp[ex][ey]){
						int p=getnum(ex,ey);
						add(k,p,INF);//中间点相连,注意顺序
					}
				}
			}
		}
	}
	int ans=HLPP();//HLPP模板
	printf("%d\n",sum-ans);//答案是总点-最大流
	return 0;
} 

HLPP模板 供大家学板子,就不统一注释了。

posted @ 2022-04-21 13:47  cunzai_zsy0531  阅读(22)  评论(0编辑  收藏  举报