[2018.3.25集训]cti-最小割

Posted zltttt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[2018.3.25集训]cti-最小割相关的知识,希望对你有一定的参考价值。

题目大意

给定一个$n*m$的网格图上
有些格子内存在炮台,每个炮台都被指定了上下左右四个方向中的某一个方向,并可以选定这个方向上的一个格子发动一次攻击。
保证没有任何炮台能攻击另一个炮台,同时炮台可以不攻击。
有些格子内存在数量为$a[i][j]$的敌人,攻击一个格子能击杀所有格子内的敌人。
定义每个炮台与它攻击的目标之间的格子为攻击轨迹。一个合法的攻击方案满足,任意两个炮台之间的攻击轨迹不相交。
求击杀敌人数最多的一种合法方案击杀的敌人数。

$n,m \leq 50,a[i][j] \leq 1000$

题解

当你做一道题,发现什么都想不到的时候,可以试一试网络流。-——出题人

考虑将格子上的权值取负,使用最小割模型。
假设不考虑攻击轨迹不相交的限制,那么可以用如下方法建图:

将每个格子拆成两个点,分别为横点和竖点。
对于每个横着的炮台,使源点连向它,将其覆盖的格子的横点串成一条链,流量为每个格子的敌人数目,最后令链上最后一个边界格子连向汇点。
对于每个竖着的炮台,使它连向汇点,同样将覆盖的格子串成一条链,但是使用的是竖点,且方向与横着时相反,并令源点向链的起点,也就是边界格子连边。
这样,建出的图的一个割对应一组攻击方案。

考虑不能相交的限制。

考虑在对应相交位置处的横点向竖点连一条$Inf$边。
此时,若横点割掉相交处的格子对应的边,则竖点割掉相交处格子及之前的边并不能使图不连通,因为横着的$Inf$边依旧可以流入。
于是,竖点只能割掉之后的边。由于竖点的边是反着建的,因此剩余可选择的边集对应原图上不相交的所有方案。

同理,若竖点割掉相交处的格子对应的边,则横点只能割掉相交处的格子对应的边之前的边,否则之间的边处的流量会通过$Inf$边流向竖点的汇点。

这个模型的来源是HNOI的切糕一题。
于是跑一边最小割即可~

代码:
(-1、-2、-3、-4分别对应上下左右)

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x*f;
}

const int N=59;
const int mint=1000;

int n,m,s,t;
int g[N][N],h[N][N];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};

inline bool in(int x,int y)
{
    return 1<=x && x<=n && 1<=y && y<=m;
}

namespace flow
{
    const int P=N*N*5;
    const int M=P*3;

    int to[M<<1],nxt[M<<1],w[M<<1],beg[P],tot=1;
    int dis[P],q[P];

    inline void adde(int u,int v,int c)
    {
        to[++tot]=v;
        nxt[tot]=beg[u];
        w[tot]=c;
        beg[u]=tot;
    }

    inline void add(int u,int v,int c)
    {
        //printf("%d %d %d\n",u,v,mint-c);
        adde(u,v,c);adde(v,u,0);
    }

    inline bool bfs()
    {
        memset(dis,-1,sizeof(dis));
        q[1]=s;dis[s]=0;
        for(int l=1,r=1,u=q[l];l<=r;u=q[++l])
            for(int i=beg[u];i;i=nxt[i])
                if(w[i]>0 && !(~dis[to[i]]))
                    dis[to[i]]=dis[u]+1,q[++r]=to[i];
        return ~dis[t];
    }

    inline int dfs(int u,int flow)
    {
        if(u==t || !flow)return flow;
        int cost=0;
        for(int i=beg[u],f;i;i=nxt[i])
            if(w[i]>0 && dis[to[i]]==dis[u]+1)
            {
                f=dfs(to[i],min(flow-cost,w[i]));
                w[i]-=f;w[i^1]+=f;cost+=f;
                if(cost==flow)break;
            }
        if(!cost)dis[u]=-1;
        return cost;
    }

    inline int dinic()
    {
        int ret=0;
        while(bfs())
            ret+=dfs(s,2e9);
        return ret;
    }
}

inline int pt(int a,int b,int d)
{
    return (((a-1)*m+b)<<1)-d;
}

int main()
{
    freopen("cti.in","r",stdin);
    freopen("cti.out","w",stdout);

    n=read();m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            g[i][j]=read();

    int totcnt=0;
    s=n*m*2+1;t=s+1;

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(g[i][j]<=-3)
            {
                flow::add(s,pt(i,j,1),mint);
                int dir=g[i][j]+4,k;
                for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++)
                {
                    int cx=i+k*dx[dir],cy=j+k*dy[dir];
                    int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir];
                    flow::add(pt(lx,ly,1),pt(cx,cy,1),mint-max(0,g[cx][cy]));
                    h[cx][cy]=dir+1;
                }
                k--;
                int cx=i+k*dx[dir],cy=j+k*dy[dir];
                flow::add(pt(cx,cy,1),t,1e8);
                totcnt++;
            }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(-2<=g[i][j] && g[i][j]<=-1)
            {
                flow::add(pt(i,j,0),t,mint);
                int dir=g[i][j]+4,k;
                for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++)
                {
                    int cx=i+k*dx[dir],cy=j+k*dy[dir];
                    int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir];
                    flow::add(pt(cx,cy,0),pt(lx,ly,0),mint-max(0,g[cx][cy]));
                    if(h[cx][cy])
                        flow::add(pt(cx-dx[h[cx][cy]-1],cy-dy[h[cx][cy]-1],1),pt(lx,ly,0),1e8);
                }
                k--;
                int cx=i+k*dx[dir],cy=j+k*dy[dir];
                flow::add(s,pt(cx,cy,0),1e8);
                totcnt++;
            }

    printf("%d\n",totcnt*mint-flow::dinic());
    return 0;
}

以上是关于[2018.3.25集训]cti-最小割的主要内容,如果未能解决你的问题,请参考以下文章

集训试题exam 信心考 最小割

[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]

FJ省队集训DAY3 T2

loj6045 「雅礼集训 2017 Day8」价

「6月雅礼集训 2017 Day11」tree

20170119