BZOJ1458 士兵占领 网络流 最大流 SAP

原文链接http://www.cnblogs.com/zhouzhendong/p/8384699.html


题目传送门 - BZOJ1458


题意概括

  有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。


题解

  对于无解的判断很简单,首先暴力判一判。

  然后首先假装全部填满,那么总个数-最大的能够删除的兵就是答案。

  我们考虑怎样构图。

  考虑一个格子的删除会影响到他所在的行和列,于是我就想到了以下构图方案。

  点:源点、汇点、每行一个行节点,每列一个列节点,每个格子一个节点。

  边:对于所有的行节点x,从源点->行x连一条边,容量为当前行在至少保留Lx个士兵的前提下最多能删掉兵的个数。

    对于所有的列节点x,从列x->汇点连一条边,容量为当前列在至少保留Cx个士兵的前提下最多能删掉兵的个数。

    对于所有的格点,从它所在行向他连一条边,从他向他所在列连一条边,如果这个点是障碍,那么这两条边容量为0,否则为1 。

  然后答案就n*m-k-MaxFlow啦。


代码

#include <cstring>
#include <cstdio>
#include <cstdlib> 
#include <cmath>
#include <algorithm>
using namespace std;
const int N=10205,M=2*2*N,INF=1e9;
struct edge{
	int x,y,cap,flow,nxt;
};
struct gragh{
	int cnt,fst[N],dist[N],n,S,T,num[N],cur[N],p[N];
	int q[N],head,tail;
	edge e[M];
	void set(int _S,int _T,int _n){
		S=_S,T=_T,n=_n,cnt=1;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b,int c){
		cnt++;
		e[cnt].x=a,e[cnt].y=b,e[cnt].cap=c,e[cnt].flow=0;
		e[cnt].nxt=fst[a],fst[a]=cnt;
		cnt++;
		e[cnt].x=b,e[cnt].y=a,e[cnt].cap=0,e[cnt].flow=0;
		e[cnt].nxt=fst[b],fst[b]=cnt;
	}
	void bfs(){
		memset(dist,-1,sizeof dist);
		head=tail=dist[T]=0;
		q[++tail]=T;
		while (head<tail)
			for (int x=q[++head],y,i=fst[x];i;i=e[i].nxt)
				if ((i&1)&&dist[y=e[i].y]==-1)
					dist[q[++tail]=y]=dist[x]+1;
		for (int i=1;i<=n;i++)
			if (dist[i]==-1)
				dist[i]=n;
	}
	void init(){
		bfs();
		memset(num,0,sizeof num);
		for (int i=1;i<=n;i++)
			num[dist[i]]++,cur[i]=fst[i];
	}
	int Augment(int &x){
		int ex_flow=INF;
		for (int i=T;i!=S;i=e[p[i]].x)
			if (e[p[i]].cap-e[p[i]].flow<=ex_flow)
				ex_flow=e[p[i]].cap-e[p[i]].flow,x=e[p[i]].x;
		for (int i=T;i!=S;i=e[p[i]].x)
			e[p[i]].flow+=ex_flow,e[p[i]^1].flow-=ex_flow;
		return ex_flow;
	}
	int ISAP(){
		int x=S,y,MaxFlow=0;
		init();
		while (dist[S]<n){
			if (x==T){
				MaxFlow+=Augment(x);
				continue;
			}
			bool found=0;
			for (int i=cur[x];i;i=e[i].nxt)
				if (dist[y=e[i].y]+1==dist[x]&&e[i].cap>e[i].flow){
					cur[x]=p[y]=i,x=y,found=1;
					break;
				}
			if (!found){
				int d=n+1;
				for (int i=fst[x];i;i=e[i].nxt)
					if (e[i].cap>e[i].flow)
						d=min(d,dist[e[i].y]+1);
				if (!--num[dist[x]])
					return MaxFlow;
				num[dist[x]=d]++,cur[x]=fst[x],x=x==S?x:e[p[x]].x;
			}
		}
		return MaxFlow;
	}
}g;
const int NM=105;
int n,m,k,S,T,p[NM][NM],L[NM],C[NM],Lz[NM],Cz[NM];
int main(){
	scanf("%d%d%d",&n,&m,&k);
	memset(p,0,sizeof p);
	for (int i=1;i<=n;i++)
		scanf("%d",&L[i]);
	for (int i=1;i<=m;i++)
		scanf("%d",&C[i]);
	for (int i=1,x,y;i<=k;i++){
		scanf("%d%d",&x,&y);
		p[x][y]=1;
	}
	memset(Lz,0,sizeof Lz);
	memset(Cz,0,sizeof Cz);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			p[i][j]^=1;
			Lz[i]+=p[i][j],Cz[j]+=p[i][j];
		}
	for (int i=1;i<=n;i++)
		if (Lz[i]<L[i]){
			puts("JIONG!");
			return 0;
		}
	for (int i=1;i<=m;i++)
		if (Cz[i]<C[i]){
			puts("JIONG!");
			return 0;
		}
	g.set(S=n*m+n+m+1,T=n*m+n+m+2,n*m+n+m+2);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			g.add(n*m+i,i*(m-1)+j,p[i][j]);
			g.add(i*(m-1)+j,n*m+n+j,p[i][j]);
		}
	for (int i=1;i<=n;i++)
		g.add(S,n*m+i,Lz[i]-L[i]);
	for (int i=1;i<=m;i++)
		g.add(n*m+n+i,T,Cz[i]-C[i]);
	printf("%d",n*m-k-g.ISAP());
	return 0;
} 

  

 

转载于:https://www.cnblogs.com/zhouzhendong/p/BZOJ1458.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值