多校15场WHU Harry Potter and the Forbidden Forest(求网络的最小割的最小边数)

哈利波特想阻止在0点得食尸鬼到达n-1点,于是要破坏几条路,每条路消耗一定魔法,他想知道在耗费最少魔法的情况下,破坏的路最少。

(错误思路)比赛时想用费用流来解,对于原边每条边的cost赋值为1,流过之后最小的费用就是最小割得割数,但是运行之后发现结果不然,原因是费用是对于当前流所过边的所有费用而言,并不是割边的费用。。。

正确解法:

(定理? (题解报告就这么给的构图方法,应该是利用了最小割的一些性质))在网络中任意流f是最大流,则该网络的所有可能存在的最小割的边一定包含在该流f的某些满流的边中。

不严密的证明(自己YY的,若有错误,希望路过的大牛指点一二):

假设存在最小割的某边不在该f中的满流边中,设该边为e1,容量为c1,流f在该边的流量为f1(c1>f1),因为f为最大流,该割为最小割,根据著名的最大流最小割定理,f的流量和该割的容量相等,则f会存在至少一条包含非e1所在边的其他可行流,且该流也不包含该割的其他边,则此可行流是独立与原割的连接源汇点的通路,与最小割的定义矛盾。

根据上面那个不成门的定理,就有了解题报告的解法,先对原网络求一遍最大流后,对残量网络的满流的边赋值为1,非满流的边赋值为inf,对新网络求一遍最大流,该流的值即为最小割的最小边数。

 

方法一:

 

#include <cstdio>
#include <cstring>

const int maxn=1050;
const int inf=1<<25;
const int s=0;
int L , W , N , P;

struct edge{
       int v,next,w;
}edge[500005];
int head[maxn],cnt;//for sap

void addedge(int u, int v, int w)
{
     edge[cnt].v=v;
     edge[cnt].w=w;
     edge[cnt].next=head[u];
     head[u]=cnt++;
     edge[cnt].v=u;
     edge[cnt].w=0;
     edge[cnt].next=head[v];
     head[v]=cnt++;
}
int sap(int t)
{
    int pre[maxn],cur[maxn],dis[maxn],gap[maxn];
    int flow=0 , aug=inf ,u;
    bool flag;
    for (int i=0 ; i<=t ; ++i)
    {
        cur[i]=head[i];
        gap[i]=dis[i]=0;
    }
    gap[s]=t+1;
    u=pre[s]=s;
    while (dis[s]<=t)
    {
          flag=0 ;
          for (int &j=cur[u] ; ~j ; j=edge[j].next)
          {
              int v=edge[j].v;
              if (edge[j].w>0 && dis[u]==dis[v]+1)
              {
                   flag=1;
                   if(edge[j].w<aug)aug=edge[j].w;
                   pre[v]=u;
                   u=v;
                   if (u==t)
                   {
                       flow+=aug;
                       while (u!=s)
                       {
                             u=pre[u];
                             edge[cur[u]].w-=aug;
                             edge[cur[u]^1].w+=aug;
                       }
                       aug=inf;
                   }
                   break;
              }
          }
          if (flag)continue ;
          int mindis=t+1;
          for (int j=head[u]; ~j ; j=edge[j].next)
          {
              int v=edge[j].v;
              if (edge[j].w>0 && dis[v]<mindis)
              {
                 mindis=dis[v];
                 cur[u]=j;
              }

          }
          if(--gap[dis[u]]==0)break;
          gap[dis[u]=mindis+1]++;
          u=pre[u];
    }
    return flow;
}


/*
void build_graph()
{

}
*/
void init ()
{
     memset (head , -1 , sizeof(head));
     cnt=0;
}

int main ()
{
    int cas;
    scanf("%d",&cas);
    int n,m;
    int u,v,w,d;
    for (int I=1 ; I<=cas ; ++I)
    {
        scanf("%d%d",&n,&m);
        init ();
        for (int i=0 ; i<m ; ++i)
        {
            scanf("%d%d%d%d",&u,&v,&w,&d);
            if(d)addedge(v,u,w);
            addedge(u,v,w);
        }
        sap(n-1);
        for (int i=0 ; i<cnt ; i+=2)
        {
            if(edge[i].w==0)
            {
                edge[i].w=1;
                edge[i^1].w=0;
            }
            else
            {
                edge[i].w=inf;
                edge[i^1].w=0;
            }
        }
        int ans=sap(n-1);
        printf("Case %d: %d\n",I,ans);
    }
    return 0;
}


 在网上又看到了一个很神的方法,给原来每条边加一个权值,原来的容量c变成c*(m+1)+1.

最小割就是cmin=c'min/(m+1),最小边数就是c'min%(m+1);

 

稍加改动 可以求出最小割的最多边数

核心代码

        for (int i=0 ; i<m ; ++i)
        {
            scanf("%d%d%I64d%d",&u,&v,&w,&d);
            if(d)addedge(v,u,w*m+1ll+w);
            addedge(u,v,w*m+1ll+w);
        }
        int ans=(sap(n-1)+m+1)%(m+1);
        //int ans=m+1-(sap(n-1)+m+1)%(m+1);
        /*求最小割的最大边数,+1改为-1*/
        printf("Case %d: %d\n",I,ans);


 

你可能感兴趣的:(多校15场WHU Harry Potter and the Forbidden Forest(求网络的最小割的最小边数))