Dinic

悲剧的POJ

Posted by JU on July 23, 2018

当前弧优化

Luogu2774

#include <bits/stdc++.h>
#define pr(p) printf("%d\n",p)
const int oo=2139063143;
const int N=1010000;
const int mod=1000000007;
using namespace std;
typedef long long LL;
typedef double db;
inline void sc (int &x)
{
    x=0; static int p; p=1; static char c; c=getchar();
    while (!isdigit(c)) { if (c=='-') p=-1; c=getchar(); }
    while ( isdigit(c)) { x=(x<<1)+(x<<3)+(c-48); c=getchar(); }
    x*=p;
}
int s,t;
struct EDGE { int v,w,nx; }lb[N<<1]; int tot=1,top[N],cur[N];
void add (int u,int v,int w)
{
    lb[++tot].v=v,lb[tot].w=w,lb[tot].nx=top[u],top[u]=tot;
    lb[++tot].v=u,lb[tot].w=0,lb[tot].nx=top[v],top[v]=tot;
}
queue <int> q;
int d[N];
bool bfs ()
{
    memset (d,0,sizeof (d));
    memcpy (cur,top,sizeof (top));
    bool ly=0;
    q.push (s),d[s]=1;
    while (q.size ())
    {
        int u=q.front (); q.pop ();
        for (int kb=top[u]; kb!=-1; kb=lb[kb].nx)
        {
            int v=lb[kb].v,w=lb[kb].w;
            //if (v<=0) continue;
            if (w&&!d[v])
            {
                d[v]=d[u]+1;
                q.push (v);
                ly|=(v==t);
            }
        }
    }
    return ly;
}
int dinic (int u,int flow)
{
    if (u==t) return flow;
    int rest=flow;
    for (; cur[u]!=-1&&rest; cur[u]=lb[cur[u]].nx)
    {
        int c=cur[u];
        int v=lb[c].v,w=lb[c].w;
        if (w&&d[v]==d[u]+1)
        {
            int k=dinic (v,min (rest,w));
            if (!k) d[v]=0;
            lb[c].w-=k;
            lb[c^1].w+=k;
            rest-=k;
            if (!rest) break;
        }
    }
    return flow-rest;
}
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m;
bool in (int x,int y) { return (x> 0)&&(y> 0)&&(x<=n)&&(y<=m); }
int bh (int x,int y) { return (x-1)*m+y; }
int main()
{
    //freopen (".in","r",stdin);
    //freopen (".out","w",stdout);
    memset (top,-1,sizeof (top));
    sc(n),sc(m);
    s=n*m+1,t=s+1;
    int ans=0;
    for (int i=1; i<=n; i++)
        for (int j=1; j<=m; j++)
        {
            int sb; sc(sb); ans+=sb;
            ((i+j)&1)?add (s,bh (i,j),sb):add (bh(i,j),t,sb);
            if ((i+j)&1)
                for (int d=0; d< 4; d++)
                    if (in (i+dx[d],j+dy[d])) add (bh (i,j),bh (i+dx[d],j+dy[d]),oo/10);
        }
    int flow;
    while ((bfs ()))
        while ((flow=dinic (s,oo))) ans-=flow;
    pr(ans);
    return 0;
}

0/1构图法

概念

n个点,可以选或不选,有相应的代价或收益。
某些点之间有特定联系。

做法

最小割。

处理单点

给每一个点x连两条边,一条S-x,一条x-T
如果割掉S-x的边,代表选点x;割掉x-T的边,代表不选点x
S-x的边权,就是选点x的代价,x-T的边权,就是不选点x的代价。
由于一个点要么选要么不选,在最小割上这两条边肯定会割掉一条。
割掉一条边后,就不可能有从S->x->T的流了,所以另外一条在最小割上不可能被割掉。

处理收益

由于最小割只能是代价,所以要把代价转换成收益。
如果选点x能得到a的收益,即不选点a的代价是失去a的收益。
一开始在答案里加上a,不选点x的代价为a,即边x-T的边权为a
即一开始默认获得所有的收益。

处理联系

依赖关系:若选点x,就必须选点y
即若选了点x,又不选点y,就要付出无穷大的代价。
选了点x,那么会割掉S-xxT连通。
不选点y,那么会割掉y-TyS连通。
那么连y-x,边权无穷大。若要只选xy中的一个,为了构成一个割,就得割掉中间的无穷大的边。 不选点x就不选点y方法类似。
要求x的状态和y相同的情况,就把二者结合起来。

放宽关系

如果选了点x,就要选点y,否则就要付出a的代价来解除这个条件。
y-x,边权a即可。

附加收益

若将一个组合中所有点都选了(或都没选),那么可以得到一定的收益。
多开一个点x,代表这个组合。
同样将收益转换成代价,若一个点没有选,就失去这个收益。
即:要选点x,就必须选组合中的所有点。
从组合中的每一个点连到点x,边权无穷大,点x连到点T,边权为收益。
若组合中有任意一个点y没有选,则y-T边会被割掉,yS连通。
由于yx一定连通,为了不让xT连通,就必须割掉x-T,失去收益。

CC 原创文章采用CC BY-NC-SA 4.0协议进行许可,转载请注明:“转载自:Dinic