题意:
有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。
M, N <= 100, 0 <= K <= M * N
题目分析:
法一:最大流
Code:
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 305
#define maxm 25005
using namespace std;
inline void read(int &a){
char c;while(!isdigit(c=getchar()));
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
const int inf = 0x3f3f3f3f;
int n,m,S,T,sum;
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],c[maxm],tot=1;
inline void line(int x,int y,int z){
nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z;
nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
}
namespace Maxflow{
int d[maxn],vd[maxn],sz;
int aug(int u,int augco)
{
if(u==T) return augco;
int need=augco,delta;
for(int i=cur[u];i;i=nxt[i]) if(c[i]&&d[u]==d[to[i]]+1){
delta=aug(to[i],min(c[i],need));
c[i]-=delta,c[i^1]+=delta;cur[u]=i;
if(!(need-=delta)||d[S]==sz) return augco-need;
}
cur[u]=fir[u];
if(!(--vd[d[u]])) d[S]=sz;
vd[++d[u]]++;
return augco-need;
}
int SAP(){
memset(d,0,sizeof d);
memset(vd,0,sizeof vd);
int flow=0;sz=T+1;
while(d[S]<sz) flow+=aug(S,inf);
return flow;
}
}
int R[maxn],C[maxn],K,x,y;
bool vis[105][105];
int main()
{
read(n),read(m),read(K);S=0,T=n+m+1;
for(int i=1;i<=n;i++) read(R[i]),line(S,i,R[i]),sum+=R[i];
for(int i=1;i<=m;i++) read(C[i]),line(n+i,T,C[i]),sum+=C[i];
while(K--) read(x),read(y),vis[x][y]=1;
for(int i=1;i<=n;i++){
x=0;
for(int j=1;j<=m;j++) if(!vis[i][j]) x++,line(i,n+j,1);
if(x<R[i]) return puts("JIONG!"),0;
}
for(int j=1;j<=m;j++){
x=0;
for(int i=1;i<=n;i++) if(!vis[i][j]) x++;
if(x<C[j]) return puts("JIONG!"),0;
}
printf("%d",sum-Maxflow::SAP());
}
法二:上下界最小流
- 每一行至少Li的流量(下界),至多列数-障碍的流量(上界设成inf也可以)
从源点向这一行代表的点连边 - 每一列同理,从这一列代表的点向汇点连边
- 每个可以在(x,y)放的点,从x行代表的点向y列代表的点连下界为0,上界为1的边
然后跑一个上下界最小流即可。
说一点第一次打上下界的实现细节:
- 最小流=初始流(下界) + 附加流 - T到S的最大流
不要忘记加上初始流 - 附加流连的边是上界容量-下界容量
不要忘记减去下界 - 容量为[0,1]的边,下界是0,不要把下界当成1。直接连容量为1的边就行了
代码。。嗯。。其实我是看着某机房dalao打的
所以自己到网上或者洛谷的题解里找吧~
可以发一个传送门