BZOJ1458 士兵占领 最大流

    这是一道网络流好题。 

    门外都在放鞭炮,没心思码字了。。 就简单说说思路。

    先判断无解的情况: 只要有一行(或列)能放士兵的格子数小于L[i](或C[i])则无解。这很显然对吧。 在有解的情况下,把所有空格子都放上士兵显然是可以满足约束条件的,但是不够优。我们要做的就是填满士兵后,再拿掉尽可能多的士兵,并要满足约束条件。

    所以可以如下构图:

    源S向每一行连边,容量为该行能放的士兵数减去L[i]

    每一列向汇T连边,容量为该列能放的士兵数减去C[i]

    对于行x和列y,若(x,y)点可以放士兵, 则x向y连边, 容量为1(因为每个格子只能放1个士兵) 

    对这个图跑最大流. 跑出来的结果是什么呢? 显然就是最大能删去的士兵数. 然后因为这个构图中已经减去了L[i]与C[i],因此每一个可行流都对应着一种合法的删士兵方案。最大流显然是合法方案中删士兵数最大的。

    答案就是所有空格子数量减去最大流,即n*m-maxflow

    祝大家新年快乐大笑

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define INF 2100000000
using namespace std;
struct node
{
    int p,next,w;
}edge[100000];
int cnt=1;
int head[10000],cur[10000],s,t,h[10000];
queue<int> q;
void insert(int a,int b,int c)
{
    edge[++cnt].p=b;
    edge[cnt].w=c;
    edge[cnt].next=head[a];
    head[a]=cnt;
    
    edge[++cnt].p=a;
    edge[cnt].w=0;
    edge[cnt].next=head[b];
    head[b]=cnt;
}
bool bfs()
{
    memset(h, 0xff, sizeof h);
    for (int i=1;i<=t;++i) cur[i]=head[i];
    h[s]=1;
    while (!q.empty()) q.pop();
    q.push(s);
    while (!q.empty())
    {
        int u=q.front();q.pop();
        for (int k=head[u];k;k=edge[k].next)
        {
            int v=edge[k].p;
            if (edge[k].w>0&&h[v]==-1)
            {
                h[v]=h[u]+1;
                q.push(v);
                if (v==t) return true;
            }
        }
    }
    return false;
}
int dfs(int u,int now)
{
    if (u==t) return now;
    int res=0,tmp;
    for (int &k=cur[u];k;k=edge[k].next)
    {
        int v=edge[k].p;
        if (edge[k].w>0&&h[v]==h[u]+1)
        {
            tmp=dfs(v,min(edge[k].w,now));
            res+=tmp;
            edge[k].w-=tmp;
            edge[k^1].w+=tmp;
            if (!(now-=tmp)) break;
        }
    }
    if (!res) h[u]=-1;
    return res;
}
int dinic()
{
    int ans=0;
    while (bfs()) ans+=dfs(s,INF);
    return ans;
}
int n,m,sum,k;
int line[200],col[200];
int ban[200][200];
int main()
{
    cin>>n>>m>>k;
    sum=n*m;
    s=n+m+1,t=n+m+2;
    for (int i=1;i<=n;++i) {scanf("%d",&line[i]);line[i]=m-line[i];}
    for (int i=1;i<=m;++i) {scanf("%d",&col[i]);col[i]=n-col[i];}
    for (int i=1;i<=k;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if (!ban[x][y])
        {
            line[x]--;
            col[y]--;
            sum--;
        }
        ban[x][y]=1;
    }
    for (int i=1;i<=n;++i)
        if (line[i]<0) {cout<<"JIONG!"<<endl;return 0;}
        else insert(s,i,line[i]);
    for (int i=1;i<=m;++i)
        if (col[i]<0) {cout<<"JIONG!"<<endl;return 0;}
        else insert(i+n,t,col[i]);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
            if (!ban[i][j]) insert(i,j+n,1);
    cout<<sum-dinic()<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值