BZOJ1458: 士兵占领 最大流

1458: 士兵占领

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 917   Solved: 515

Description

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

Input

第一行两个数M, N, K分别表示棋盘的行数,列数以及障碍的个数。 第二行有M个数表示Li。 第三行有N个数表示Ci。 接下来有K行,每行两个数X, Y表示(X, Y)这个格子是障碍。

Output

输出一个数表示最少需要使用的士兵个数。如果无论放置多少个士兵都没有办法占领整个棋盘,输出”JIONG!” (不含引号)

Sample Input

4 4 4
1 1 1 1
0 1 0 3
1 4
2 2
3 3
4 3

Sample Output

4
数据范围
M, N <= 100, 0 <= K <= M * N

题解:

题目显然可以用网络流来求解:
因为想要得到最少需要摆放的士兵,我们可以转化一下问题去求最多有多少个格子不能放
我们可以把所有列,所有行抽象成n+m个点,把每一行向源点连一条边
边权是:对于每一行看看有多少个位置可以放士兵(也就是假设每排可以放m个士兵,这排有x个地方不能放士兵,这排要求必须要放y,边权就是m-x-y),对于每一列也是同理。
然后我们再搜索整个棋盘,比如坐标(x,y)处可以放棋子,就将行x所代表的点和列y所代表的点连一条边,边权为1,跑最大流就可以了,最大流的意义就是最多能找到多少个点可以不放棋子
答案ans=(n*m-k-最大流)
#include<cstdio>
#include<queue>
#include<iostream>
#include<cstring>
using namespace std;
const int N=305;
const int M=40005;
const int inf=2100000000;
int C[N],L[N],n,m,k,nl[N],nc[N],ans,mp[N][N];
int from[M],to[M],nxt[M],lj[N],cnt=-1,w[M],pre[N];
void add(int f,int t,int p)
{
	cnt++;
	to[cnt]=t;
	nxt[cnt]=lj[f];
	lj[f]=cnt;
	w[cnt]=p;
	cnt++;
	to[cnt]=f;
	nxt[cnt]=lj[t];
	lj[t]=cnt;
	w[cnt]=0;
}
int S,T,d[N];
queue<int>Q;
bool bfs()
{
	memset(d,0,sizeof(d));
	d[0]=1;
	Q.push(0);
	while(!Q.empty())
	{
		int x=Q.front();
		Q.pop();
		for(int i=lj[x];i>=0;i=nxt[i])
		if(w[i]&&!d[to[i]])
		{
			d[to[i]]=d[x]+1;
			Q.push(to[i]);
		}
	}
	if(d[T]) return true;
	return false;
}
int dfs(int x,int v)
{
	if(x==T||v==0) return v;
	int ret=0;
	for(int i=lj[x];i>=0;i=nxt[i])
	if(d[to[i]]==d[x]+1)
	{
		int f=dfs(to[i],min(w[i],v));
		w[i]-=f;
		w[i^1]+=f;
		v-=f;
		ret+=f;
		if(v==0) break;
	}
	return ret;
}
void make()
{
	for(int i=1;i<=n;i++) add(S,i,m-L[i]-nl[i]);
	for(int i=1;i<=m;i++) add(i+n,T,n-C[i]-nc[i]);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	if(!mp[i][j]) add(i,j+n,1);
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	S=0,T=n+m+1;
	for(int i=S;i<=T;i++) lj[i]=-1;
	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;i<=k;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		nl[x]++,nc[y]++;
		mp[x][y]=1;
	}
	make();
	for(int i=0;i<=cnt;i++)
	if(w[i]<0)
	{
		printf("JIONG");
		return 0;
	}
	while(bfs()) ans+=dfs(S,inf);
	printf("%d",n*m-k-ans);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值