BZOJ 1458: 士兵占领

BZOJ 1458: 士兵占领

标签(空格分隔): OI BZOJ 上下界网络流 最小流


Time Limit: 10 Sec
Memory Limit: 64 MB


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


Solution####

诶省选一试前最后一个晚上调这个搞到11点,因为n、m反了,我的程序里n、m与题目意义相反
建立M个点 (A_i)表示行的放置数量,N个点(B_j)表示列的放置数量。
S向(A_i)连一条下界为(L_i),上界无穷大的边;
(B_j)向T连一条下界为(C_j),上界无穷大的边。
若(i,j)可以放置士兵则在(A_i、B_j)间连一条上界为1下界为0的边。
跑一边上下界最小流即可
上下界网络流
新建超级源SS,超级汇TT。
设点i的入边下界和为(In_i)
设点i的出边下界和为(Out_i)
SS向i连上界为(In_i),i向TT连上界为(Out_i)的边
对原图中的边,设上界为up,下界为down,则改为残余网络,即下界为0,上界为up-down
T向S连一条上界无穷大的边
SS向TT跑一边最大流得到可行流
T到S的边的流量为原图的总流量
为了使总流量最小所以要使得(E_{T->S})最小。删除边T->S,尽量退流,即跑一遍T


Code####

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
int read()
{
	int s=0,f=1;char ch=getchar();
	while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
	while('0'<=ch&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
	return s*f;
}
int S,T,SS,TT,n,m,K,np;
int A[105],B[105],jr[10005],jc[10005];
int be[100005],bn[200005],bv[200005],bl[200005],bw=1;
void put(int u,int v,int l)
{bw++;bn[bw]=be[u];be[u]=bw;bv[bw]=v;bl[bw]=l;}
int d[100005];
bool spfa(int S,int T)
{
	for(int i=1;i<=np;i++)
	   d[i]=10000000;
	d[S]=1;
	queue<int>q;
	for(q.push(S);!q.empty();)
	   {int u=q.front();q.pop();
	    for(int i=be[u],v;i;i=bn[i])
	       if(d[v=bv[i]]>d[u]+1&&bl[i])
	         {d[v]=d[u]+1;
	          q.push(v);
			 }
	   }
	return d[T]!=10000000;
}
int ans;
int dinic(int u,int mf,int T)
{
	if(mf==0)return 0;
	if(u==T)return mf;
	int sum=0;
	for(int i=be[u],v;i;i=bn[i])
	   if(d[v=bv[i]]==d[u]+1)
	     {int f=dinic(v,min(mf-sum,bl[i]),T);
	      bl[i]-=f;
	      bl[i^1]+=f;
	      sum+=f;
	     }
	return sum;
}
bool p[101][101];
int main()
{
	m=read(),n=read(),K=read();
	S=++np,T=++np;
	for(int i=1;i<=m;i++)
	   {int l=read();
	    put(S,A[i]=++np,1e9);
	    put(A[i],S,0);
	    jr[np]+=l;
	    jc[S]+=l;
	   }
	for(int i=1;i<=n;i++)
	   {int l=read();
	    put(B[i]=++np,T,1e9);
	    put(T,B[i],0);
	    jc[np]+=l;
	    jr[T]+=l;
	   }
	for(int i=1;i<=K;i++)
	   {int x=read(),y=read();p[x][y]=1;}
	for(int i=1;i<=m;i++)
	   for(int j=1;j<=n;j++)
	      if(p[i][j]==0)
	        put(A[i],B[j],1),put(B[j],A[i],0);
	SS=++np,TT=++np;
	int sumr=0;
	for(int i=1;i<=np;i++)
	   {if(jr[i]-jc[i]>0)put(SS,i,jr[i]-jc[i]),put(i,SS,0),sumr+=jr[i]-jc[i];
	    else put(i,TT,jc[i]-jr[i]),put(TT,i,0);
	   }
	put(T,S,1e9);
	put(S,T,0);
	while(spfa(SS,TT))ans+=dinic(SS,1e9,TT);
	if(ans<sumr)
	  {printf("JIONG!
");return 0;}
	ans=bl[bw];
	bl[bw]=bl[bw-1]=0;
	while(spfa(T,S))ans-=dinic(T,1e9,S);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/wuyuhan/p/5239297.html