BZOJ 1458: 士兵占领 【最大流/上下界最小流】

题意:

有一个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打的
所以自己到网上或者洛谷的题解里找吧~
可以发一个传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值