51Nod 1499 图(最小割)

来源:互联网 发布:freebsd和ubuntu 编辑:程序博客网 时间:2024/05/21 10:21



        中文题。

        说实话,确实没有想到这题可以用最小割来玩……

        看了题解才知道,确实是有那么点像论文里面最大最小模型对应的最小割。

        先说说具体做法吧。首先,对于所有的点i,我都向所有的其他点j连边,流量为|i-j|。然后,根据原图中的连通情况,对于任意两个点u,v,若存在边u->v,那么连边s->u和s->v流量都为|u-v|,反之则连边u->t和v->t流量都为|u-v|。最后的(ΣΣ|i-j|-mincut)/2就是最后结果。

        下面,我就自己的理解说说为什么吧。首先,我们计算一个完全图的完美值,显然最大是ΣΣ|i-j|/2。但是实际上却不会这么完美,我们最后分成的两个集合的完美值会少很多,那么少的是哪些部分呢?对于A集合,少的是A中任意两个不相连的点的边权值,因为计算A的时候只会算相连的权值,所以少了不相连的权值;对于B集合,少的是B中任意两个相连的点的边权值,因为计算B的时候只会计算不相连的权值,所以少了相连的权值;另外还有就是A中的任意一个点和B中任意一个点连边的边权值,这个在完全图中会计算但是分成两部分之后少计算了。换句话说,完全图中是所有的边的权值都算了,但是给你的图不一定是完全图,所以有些边不能算,于是我们就要在完全图的基础上减去这些边。因此,我们就用之前说的方法建图,由于我们求的是最大的完美值,所以就要减去最小的边权,减去最少的这些没有计算到的边,所以对应最小割模型。

        关于这个最小割,我再说说含义,如果最小割中有边s->i意味着i属于B集合,即i属于B集合,所以就要减去与i相连的点之间的权值;若最小割中有边i->t,意味着i属于A集合,即i属于A集合,所以就要减去与i不相连的点之间的权值;若最小割中有边i->j,意味着i和j不在同一个集合中,所以要减去他们两个产生的权值。又由于最小割中不存在s到t的路径,所以可以保证某一个点i不会即属于A集合也属于B集合。如此就可以确定算法的正确性。说的可能还不是很清楚,有问题欢迎提出。具体见代码:

#include<bits/stdc++.h>#define INF 0x3f3f3f3f#define M 4000010#define N 2010namespace ISAP{    int H[N],d[N],cur[N],pre[N],gap[N],Q[M];    struct Edge{short u,v,c;int n;} E[M];    int nv,flow,head,tail,cntE,f;    void init(){cntE=0; memset(H,-1,sizeof(H));}    void addedge(int u,int v,int c)    {        E[cntE]=Edge{u,v,c,H[u]}; H[u]=cntE++;        E[cntE]=Edge{v,u,0,H[v]}; H[v]=cntE++;    }    void revbfs(int s,int t)    {        head=tail=0 ;        memset(d,-1,sizeof(d));        memset(gap,0,sizeof(gap));        Q[tail++]=t;d[t]=0;gap[d[t]]=1;        while (head!=tail)        {            int u=Q[head++];            for (int i=H[u];~i;i=E[i].n)            {                int v=E[i].v; if (~d[v]) continue;                d[v]=d[u]+1; gap[d[v]]++; Q[tail++]=v;            }        }    }    int isap(int s,int t)    {        memcpy(cur,H,sizeof(cur)); nv=t;        flow=0; revbfs(s,t); int u=pre[s]=s,i;        while (d[s]<nv)        {            if (u==t)            {                f=INF;                for (i=s;i!=t;i=E[cur[i]].v)                    if (f>E[cur[i]].c) f=E[cur[i]].c,u=i;                flow += f;                for (i=s;i!=t;i=E[cur[i]].v)                    E[cur[i]].c-=f,E[cur[i]^1].c+=f;            }            for (i=cur[u];~i;i=E[i].n)                if (E[i].c&&d[u]==d[E[i].v]+1) break ;            if (~i) cur[u]=i,pre[E[i].v]=u,u=E[i].v;            else            {                if (0==--gap[d[u]]) break ;                int minv=nv,v;                for (int i=H[u];~i;i=E[i].n)                {                    v=E[i].v;                    if (E[i].c&&minv>d[v]) minv=d[v],cur[u]=i;                }                d[u]=minv+1; gap[d[u]]++; u=pre[u];            }        }        return flow ;    }}using namespace ISAP;bool vis[N][N];int n,m,sum,s,t;int main(){while(~scanf("%d%d",&n,&m)){    init(); sum=0;    s=n+1; t=n+2;for(int i=1;i<=m;i++)        {            int u,v;            scanf("%d%d",&u,&v);            vis[u][v]=vis[v][u]=1;            addedge(s,u,abs(u-v));            addedge(s,v,abs(u-v));            sum+=abs(u-v)<<1;        }        for(int i=1;i<=n;i++)            for(int j=i+1;j<=n;j++)            {                if (!vis[i][j])                {                    addedge(i,t,abs(i-j));                    addedge(j,t,abs(i-j));                    sum+=abs(i-j)<<1;                } else vis[i][j]=vis[j][i]=0;                addedge(i,j,abs(i-j));                addedge(j,i,abs(i-j));            }        printf("%d\n",(sum-isap(s,t))/2);}return 0;}