题目大意
有N*M的网格,有一些障碍格子,障碍格子不能放士兵。
非障碍格子最多放一个士兵,请放置最少的士兵使得第i行至少有a[i]个士兵第i列至少有b[i]个士兵。
上下界最小流
很明显了,第i行用点i表示,第i列用点i’表示。(i,j)不是障碍格子,那么i向j’连下限为0上限为1的边。
第i行如果需要至少j个士兵,s向i连下限为j上限为inf的边。
第i列如果需要至少j个士兵,i’向t连下限为j上限为inf的边。
然后做最小流。
要判无解的情况,我们可以做完最小流后再去检验是否有可行流。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=300+10,maxm=50000+10,inf=10000000;
int d[maxn],h[maxn],now[maxn],go[maxm*2],dis[maxm*2],next[maxm*2],fx[maxm*2];
bool bz[maxn],pd[maxn][maxn];
int i,j,k,l,r,s,t,ss,tt,n,m,p,ans,tot;
bool czy;
void add(int x,int y,int z,int d){
go[++tot]=y;
dis[tot]=z;
fx[tot]=tot+d;
next[tot]=h[x];
h[x]=tot;
}
void link(int x,int y,int l,int r){
add(ss,y,l,1);
add(y,ss,0,-1);
add(x,tt,l,1);
add(tt,x,0,-1);
add(x,y,r-l,1);
add(y,x,0,-1);
}
int dfs(int x,int flow){
bz[x]=1;
if (x==tt){
ans+=flow;
return flow;
}
int r=now[x],k;
while (r){
if (!bz[go[r]]&&dis[r]&&d[x]==d[go[r]]+1){
k=dfs(go[r],min(flow,dis[r]));
if (k){
dis[r]-=k;
dis[fx[r]]+=k;
now[x]=r;
return k;
}
}
r=next[r];
}
now[x]=0;
return 0;
}
bool change(){
int tmp=inf,i,r;
fo(i,ss,tt)
if (bz[i]){
r=h[i];
while (r){
if (!bz[go[r]]&&dis[r]&&d[go[r]]+1-d[i]<tmp) tmp=d[go[r]]+1-d[i];
r=next[r];
}
}
if (tmp==inf) return 0;
fo(i,ss,tt)
if (bz[i]) d[i]+=tmp;
return 1;
}
int main(){
scanf("%d%d%d",&n,&m,&p);
ss=1;s=2;t=n+m+2;tt=n+m+3;
fo(i,1,n){
scanf("%d",&j);
link(s,i+2,j,inf);
}
fo(i,1,m){
scanf("%d",&j);
link(i+n+2,t,j,inf);
}
fo(i,1,p){
scanf("%d%d",&j,&k);
pd[j][k]=1;
}
fo(i,1,n)
fo(j,1,m)
if (!pd[i][j]) link(i+2,j+n+2,0,1);
do{
fo(i,ss,tt) now[i]=h[i];
fill(bz+ss,bz+tt+1,0);
while (dfs(ss,inf)) fill(bz+ss,bz+tt+1,0);
}while (change());
ans=0;
//fill(d+ss,d+tt+1,0);
link(t,s,0,inf);
do{
fo(i,ss,tt) now[i]=h[i];
fill(bz+ss,bz+tt+1,0);
while (dfs(ss,inf)) fill(bz+ss,bz+tt+1,0);
}while (change());
czy=1;
r=h[ss];
while (r){
if (dis[r]){
czy=0;
break;
}
r=next[r];
}
if (czy) printf("%d\n",ans);else printf("JIONG!\n");
}