BZOJ1458士兵占领

68 篇文章 0 订阅
23 篇文章 0 订阅

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

一眼看过去,这不是带下界的网络流吗?

然后各种GG.

原来下界可以通过减法转为上界,还顺便把大小关系转换过来了。

ACcode:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 105
#define maxp 205
#define inf 0x3f3f3f3f
#define maxm maxn*maxn*10
using namespace std;

int n,m,k;
int info[maxp],to[maxm],Prev[maxm],cap[maxm],cnt_e=1;
inline void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
inline void Line(int u,int v,int c){ Node(u,v,c),Node(v,u,0); }

int S,T,stm,vd[maxp],d[maxp],w[2][maxn];
bool ban[maxp][maxp];

int aug(int now,int Max)
{
	if(now==T)
		return Max;
	int inc,Min=T-1,st=Max;
	for(int i=info[now];i;i=Prev[i])
		if(cap[i])
		{
			if(d[to[i]]+1==d[now])
			{
				inc=aug(to[i],min(cap[i],st));
				st-=inc,cap[i]-=inc,cap[i^1]+=inc;
				if(!st || d[S]>=T) return Max-st;
			}
			Min=min(Min,d[to[i]]);
		}
	if(Max==st)
	{
		(!--vd[d[now]]) && (d[S]=T);
		++vd[d[now]=Min+1];
	}
	return Max-st;
}

int sap()
{
	memset(vd,0,sizeof vd);
	memset(d,0,sizeof d);
	for(vd[0]=T,stm=0;d[S]<T;)
		stm+=aug(S,inf);
	return stm;
}

int main()
{
	int u,v;
	scanf("%d%d%d",&n,&m,&k);
	S=n+m+1,T=S+1;
	for(int i=1;i<=n;i++) scanf("%d",&w[0][i]),w[0][i]=m-w[0][i];
	for(int i=1;i<=m;i++) scanf("%d",&w[1][i]),w[1][i]=n-w[1][i];
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d",&u,&v);
		w[0][u]--,w[1][v]--;
		ban[u][v+n]=1;
	}
	bool ERROR=0;
	for(int i=1;i<=n;i++) 
		if(w[0][i]<0){ ERROR=1;break;}
		else Line(S,i,w[0][i]);
	if(ERROR){ puts("JIONG!");return 0; }
	for(int i=1;i<=m;i++)
		if(w[1][i]<0){ ERROR=1;break; }
		else Line(i+n,T,w[1][i]);
	if(ERROR){ puts("JIONG!");return 0; }
	for(int i=1;i<=n;i++)
		for(int j=1+n;j<=n+m;j++)
			if(!ban[i][j])
				Line(i,j,inf);
				
	printf("%d\n",n*m-k-sap());
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值