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!\n");return 0;}
ans=bl[bw];
bl[bw]=bl[bw-1]=0;
while(spfa(T,S))ans-=dinic(T,1e9,S);
printf("%d\n",ans);
return 0;
}