• bzoj 5120 无限之环 & 洛谷 P4003 —— 费用流(多路增广SPFA)


    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120

    https://www.luogu.org/problemnew/show/P4003

    神奇的费用流建图;

    首先,网格图,相邻之间有关系,所以先二分染色一下;

    然后发现问题就是染色后黑白点之间要完美匹配插头;

    所以可以考虑把旋转通过带一些代价变成插头方向的变化;

    把一个格子拆成上下左右四个点,分类讨论,连边即可;

    然而一开始连边竟然写了100多行...然后T了;

    看了看题解代码,突然悟到了一些压行的方式,于是把建图压成了40行;

    但还是T了,于是又改来改去,还把题解的建图粘过来囧,然后WA了;

    这250行左右的代码就是一下午的辛酸调试:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int const xn=1e5,xm=4e5,inf=1e9;//
    int n,m,hd[xn],ct=1,to[xm],nxt[xm],w[xn],c[xn];
    int dis[xn],pre[xn],inc[xn],S,T;
    bool vis[xn];
    queue<int>q;
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    int Min(int x,int y){return x<y?x:y;}
    void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
    void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,0);}
    int d[5],bin[5];
    int id(int x,int y,int k){return ((x-1)*m+y-1)*4+k;}
    inline int get(int x)
    {
      int ret=0;
      for(int i=1;i<=4;i++)
        if(x&bin[i-1])d[i]=1,ret++; else d[i]=0;
      return ret;
    }
    inline int op(int x){if(x<=2)return x+2; return x-2;}
    inline bool ck(){return (d[1]&&d[3]&&!d[2]&&!d[4])||(d[2]&&d[4]&&!d[1]&&!d[3]);}
    inline bool bfs()
    {
      for(int i=S;i<=T;i++)vis[i]=0;
      for(int i=S;i<=T;i++)dis[i]=inf;
      dis[S]=0; inc[S]=inf; vis[S]=1; q.push(S);
      while(q.size())
        {
          int x=q.front(); q.pop(); vis[x]=0;
          //printf("x=%d d=%d
    ",x,dis[x]);
          for(int i=hd[x],u;i;i=nxt[i])
        if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
          {
            //if(w[i]<0)printf("i=%d w=%d c=%d
    ",i,w[i],c[i]);
            dis[u]=dis[x]+w[i]; pre[u]=i;
            inc[u]=Min(inc[x],c[i]);
            if(!vis[u])vis[u]=1,q.push(u);
          }
        }
      //printf("dis[%d]=%d
    ",T,dis[T]);
      return dis[T]!=inf;
    }
    inline void up()
    {
      int x=T;
      while(x!=S)
        {
          int i=pre[x];
          c[i]-=inc[T]; c[i^1]+=inc[T];
          x=to[i^1];
        }
    }
    int main()
    {
      n=rd(); m=rd(); S=0; T=n*m*4+1;
      bin[0]=1; for(int i=1;i<=3;i++)bin[i]=(bin[i-1]<<1);
      int goal=0;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
          {
        int x=rd(),u=0; int t=(((i+j)&1)==0);
        for (int k=1;k<=4;k++)
          {
            int nw=id(i,j,k);
            if (x&(1<<(k-1))) u++,t?add(S,nw,0,1):add(nw,T,0,1);
          }
        goal+=u;
        if (x==5||x==10) continue;
        if (u==1||u==3) for (int k=1;k<=4;k++) add(id(i,j,k),id(i,j,k+1&3),1,1),add(id(i,j,k+1&3),id(i,j,k),1,1);
        if (u==2)
          {
            add(id(i,j,1),id(i,j,3),1,1),add(id(i,j,3),id(i,j,1),1,1);
            add(id(i,j,2),id(i,j,4),1,1),add(id(i,j,4),id(i,j,2),1,1);
          }
          }
        if (goal&1) {puts("-1");return 0;}goal>>=1;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
          if (((i+j)&1)==0)
            {
          if (i>1) add(id(i,j,1),id(i-1,j,3),0,1);
          if (j<m) add(id(i,j,2),id(i,j+1,4),0,1);
          if (i<n) add(id(i,j,3),id(i+1,j,1),0,1);
          if (j>1) add(id(i,j,4),id(i,j-1,2),0,1);
            }
      /*
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          {
        int x=rd(); int cnt=get(x); goal+=cnt;
        bool t=(((i+j)&1)==0);
        if(t){for(int k=1;k<=4;k++)if(d[k])add(S,id(i,j,k),0,1);}
        else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),T,0,1);}
        if(x==5||x==10)continue;
        if(cnt==1)
          {
            int nw; for(int k=1;k<=4;k++)if(d[k]){nw=k; break;}
            if(t){for(int k=1;k<=4;k++)if(!d[k])add(nw,id(i,j,k),(k==op(nw)?2:1),1);}
            else {for(int k=1;k<=4;k++)if(!d[k])add(id(i,j,k),nw,(k==op(nw)?2:1),1);}
          }
        if(cnt==3)
          {
            int nw; for(int k=1;k<=4;k++)if(!d[k]){nw=k; break;}
            if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),nw,(k==op(nw)?2:1),1);}
            else {for(int k=1;k<=4;k++)if(d[k])add(nw,id(i,j,k),(k==op(nw)?2:1),1);}
          }
        if(cnt==2)
          {
            if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),id(i,j,op(k)),1,1);}
            else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,op(k)),id(i,j,k),1,1);}
          }
          }
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          if(((i+j)&1)==0)
        {
          if(i>1)add(id(i,j,1),id(i-1,j,3),0,1);
          if(j<m)add(id(i,j,2),id(i,j+1,4),0,1);
          if(i<n)add(id(i,j,3),id(i+1,j,1),0,1);
          if(j>1)add(id(i,j,4),id(i,j-1,2),0,1);
        }
    */
        /*
        if(((i+j)&1)==0)//black
          {
            if(!ck())//!straight
              {
            if(cnt==1)
              {
                for(int k=1;k<=4;k++)
                  if(d[k])
                {
                  int nw=id(i,j,k); add(S,nw,0,1);
                  for(int l=1;l<=4;l++)
                    if(l==k)continue;
                    else if(l==op(k))add(nw,id(i,j,l),2,1);
                    else add(nw,id(i,j,l),1,1);
                  break;
                }
                }
            else if(cnt==2)
              {
                for(int k=1;k<=4;k++)
                  if(d[k])
                {
                  add(S,id(i,j,k),0,1);
                  add(id(i,j,k),id(i,j,op(k)),1,1);
                }
              }
            else if(cnt==3)
              {
                for(int k=1;k<=4;k++)
                  if(d[k])add(S,id(i,j,k),0,1);
                  else 
                {
                  for(int l=1,nw=id(i,j,k);l<=4;l++)
                    if(l==k)continue;
                    else if(l==op(k))add(id(i,j,l),nw,2,1);
                    else add(id(i,j,l),nw,1,1);
                }
              }
            else if(cnt==4)
              for(int k=1;k<=4;k++)add(S,id(i,j,k),0,1);
            if(i-1)add(id(i,j,1),id(i-1,j,3),0,1);
            if(j<m)add(id(i,j,2),id(i,j+1,4),0,1);
            if(i<n)add(id(i,j,3),id(i+1,j,1),0,1);
            if(j-1)add(id(i,j,4),id(i,j-1,2),0,1);
              }
            else//straight
              {
            for(int k=1;k<=4;k++)
              if(d[k])add(S,id(i,j,k),0,1);
            if(i-1&&d[1])add(id(i,j,1),id(i-1,j,3),0,1);
            if(j<m&&d[2])add(id(i,j,2),id(i,j+1,4),0,1);
            if(i<n&&d[3])add(id(i,j,3),id(i+1,j,1),0,1);
            if(j-1&&d[4])add(id(i,j,4),id(i,j-1,2),0,1);
              }
          }
        else//white
          {
            if(!ck())//!straight
              {
            if(cnt==1)
              {
                for(int k=1;k<=4;k++)
                  if(d[k])
                {
                  int nw=id(i,j,k); add(nw,T,0,1);
                  for(int l=1;l<=4;l++)
                    if(l==k)continue;
                    else if(l==op(k))add(id(i,j,l),nw,2,1);
                    else add(id(i,j,l),nw,1,1);
                  break;
                }
              }
            else if(cnt==2)
              {
                for(int k=1;k<=4;k++)
                  if(d[k])
                {
                  add(id(i,j,k),T,0,1);
                  add(id(i,j,op(k)),id(i,j,k),1,1);
                }
              }
            else if(cnt==3)
              {
                for(int k=1;k<=4;k++)
                  if(d[k])add(id(i,j,k),T,0,1);
                  else
                {
                  int nw=id(i,j,k);
                  for(int l=1;l<=4;l++)
                    if(l==k)continue;
                    else if(l==op(k))add(nw,id(i,j,l),2,1);
                    else add(nw,id(i,j,l),1,1);
                }
              }
            else if(cnt==4)
              for(int k=1;k<=4;k++)add(id(i,j,k),T,0,1);
              }
            else//straight
              {
            for(int k=1;k<=4;k++)
              if(d[k])add(id(i,j,k),T,0,1);
              }
          }
          }
        */
      //if(goal&1){puts("-1"); return 0;} goal>>=1;
      int ans=0,flow=0;
      while(bfs())ans+=dis[T]*inc[T],flow+=inc[T],up();
      if(flow==goal)printf("%d
    ",ans);
      else puts("-1");
      return 0;
    }

    突然发现连边里 nw 和 id(i,j,nw) 不分了,改过来后竟然——就A了!

    但是空间必须开 4e5?明明我一开始算的应该是 4e4 ...

    然而这篇代码在洛谷上一交,全是TLE...似乎还应该写多路增广SPFA...

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int const xn=1e5,xm=4e5,inf=1e9;
    int n,m,hd[xn],ct=1,to[xm],nxt[xm],w[xn],c[xn];
    int dis[xn],pre[xn],inc[xn],S,T;
    bool vis[xn];
    queue<int>q;
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    int Min(int x,int y){return x<y?x:y;}
    void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
    void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,0);}
    int d[5],bin[5];
    int id(int x,int y,int k){return ((x-1)*m+y-1)*4+k;}
    inline int get(int x)
    {
      int ret=0;
      for(int i=1;i<=4;i++)
        if(x&bin[i-1])d[i]=1,ret++; else d[i]=0;
      return ret;
    }
    inline int op(int x){if(x<=2)return x+2; return x-2;}
    inline bool ck(){return (d[1]&&d[3]&&!d[2]&&!d[4])||(d[2]&&d[4]&&!d[1]&&!d[3]);}
    inline bool bfs()
    {
      for(int i=S;i<=T;i++)dis[i]=inf;
      dis[S]=0; inc[S]=inf; vis[S]=1; q.push(S);
      while(q.size())
        {
          int x=q.front(); q.pop(); vis[x]=0;
          for(int i=hd[x],u;i;i=nxt[i])
        if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
          {
            dis[u]=dis[x]+w[i]; pre[u]=i;
            inc[u]=Min(inc[x],c[i]);
            if(!vis[u])vis[u]=1,q.push(u);
          }
        }
      return dis[T]!=inf;
    }
    inline void up()
    {
      int x=T;
      while(x!=S)
        {
          int i=pre[x];
          c[i]-=inc[T]; c[i^1]+=inc[T];
          x=to[i^1];
        }
    }
    int main()
    {
      n=rd(); m=rd(); S=0; T=n*m*4+1;
      bin[0]=1; for(int i=1;i<=3;i++)bin[i]=(bin[i-1]<<1);
      int goal=0;
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          {
        int x=rd(); int cnt=get(x); goal+=cnt;
        bool t=(((i+j)&1)==0);
        if(t){for(int k=1;k<=4;k++)if(d[k])add(S,id(i,j,k),0,1);}
        else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),T,0,1);}
        if(x==5||x==10)continue;
        if(cnt==1)
          {
            int nw; for(int k=1;k<=4;k++)if(d[k]){nw=k; break;}
            if(t){for(int k=1;k<=4;k++)if(!d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?2:1),1);}//id(i,j,nw)!!
            else {for(int k=1;k<=4;k++)if(!d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?2:1),1);}
          }
        if(cnt==3)
          {
            int nw; for(int k=1;k<=4;k++)if(!d[k]){nw=k; break;}
            if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?2:1),1);}
            else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?2:1),1);}
          }
        if(cnt==2)
          {
            if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),id(i,j,op(k)),1,1);}
            else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,op(k)),id(i,j,k),1,1);}
          }
          }
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          if(((i+j)&1)==0)
        {
          if(i>1)add(id(i,j,1),id(i-1,j,3),0,1);
          if(j<m)add(id(i,j,2),id(i,j+1,4),0,1);
          if(i<n)add(id(i,j,3),id(i+1,j,1),0,1);
          if(j>1)add(id(i,j,4),id(i,j-1,2),0,1);
        }
      if(goal&1){puts("-1"); return 0;} goal>>=1;
      int ans=0,flow=0;
      while(bfs())ans+=dis[T]*inc[T],flow+=inc[T],up();
      if(flow==goal)printf("%d
    ",ans);
      else puts("-1");
      return 0;
    }
    单路增广SPFA

    于是学习了一下多路增广SPFA的写法,似乎就是用SPFA跑出一些可行路线,然后 dinic,据说在边多流量少的图上作用很好;

    竟然一下子跑得飞快,吓了一跳!这时间根本不是一个层次的啊!实在是妙!

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int const xn=1e5,xm=4e5,inf=1e9;
    int n,m,hd[xn],ct=1,to[xm],nxt[xm],w[xn],c[xn];
    int dis[xn],pre[xn],S,T,cur[xn],ans;
    bool vis[xn];
    queue<int>q;
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    int Min(int x,int y){return x<y?x:y;}
    void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
    void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,0);}
    int d[5],bin[5];
    int id(int x,int y,int k){return ((x-1)*m+y-1)*4+k;}
    inline int get(int x)
    {
      int ret=0;
      for(int i=1;i<=4;i++)
        if(x&bin[i-1])d[i]=1,ret++; else d[i]=0;
      return ret;
    }
    inline int op(int x){if(x<=2)return x+2; return x-2;}
    inline bool ck(){return (d[1]&&d[3]&&!d[2]&&!d[4])||(d[2]&&d[4]&&!d[1]&&!d[3]);}
    bool SPFA()
    {
        for(int i=S;i<=T;i++)vis[i]=0;
        for(int i=S;i<=T;i++)dis[i]=inf;
        dis[S]=0, q.push(S);
        while(q.size())
          {
            int x=q.front(); q.pop(), vis[x]=0;
            for(int i=hd[x],u;i;i=nxt[i])
          if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
            {
              dis[u]=dis[x]+w[i];
              if(!vis[u])vis[u]=1,q.push(u);
            }
          }
        return dis[T]!=inf;
    }
    int dfs(int x,int fl)
    {
        if(x==T)return fl;
        vis[x]=1;
        for(int &i=cur[x],u,tmp;i;i=nxt[i])
            if(!vis[u=to[i]]&&c[i]&&dis[u]==dis[x]+w[i])
          if(tmp=dfs(u,Min(fl,c[i])))
            {c[i]-=tmp,c[i^1]+=tmp,ans+=w[i]*tmp; return tmp;}
        return 0;
    }
    int MCMF()
    {
        int ret=0;
        while(SPFA())
        {
            for(int i=S;i<=T;i++)cur[i]=hd[i];
        int tmp;
            while(tmp=dfs(S,inf))ret+=tmp;
        }
        return ret;
    }
    int main()
    {
      n=rd(); m=rd(); S=0; T=n*m*4+1;
      bin[0]=1; for(int i=1;i<=3;i++)bin[i]=(bin[i-1]<<1);
      int goal=0;
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          {
        int x=rd(); int cnt=get(x); goal+=cnt;
        bool t=(((i+j)&1)==0);
        if(t){for(int k=1;k<=4;k++)if(d[k])add(S,id(i,j,k),0,1);}
        else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),T,0,1);}
        if(x==5||x==10)continue;
        if(cnt==1)
          {
            int nw; for(int k=1;k<=4;k++)if(d[k]){nw=k; break;}
            if(t){for(int k=1;k<=4;k++)if(!d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?2:1),1);}//id(i,j,nw)!!
            else {for(int k=1;k<=4;k++)if(!d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?2:1),1);}
          }
        if(cnt==3)
          {
            int nw; for(int k=1;k<=4;k++)if(!d[k]){nw=k; break;}
            if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),id(i,j,nw),(k==op(nw)?2:1),1);}
            else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,nw),id(i,j,k),(k==op(nw)?2:1),1);}
          }
        if(cnt==2)
          {
            if(t){for(int k=1;k<=4;k++)if(d[k])add(id(i,j,k),id(i,j,op(k)),1,1);}
            else {for(int k=1;k<=4;k++)if(d[k])add(id(i,j,op(k)),id(i,j,k),1,1);}
          }
          }
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          if(((i+j)&1)==0)
        {
          if(i>1)add(id(i,j,1),id(i-1,j,3),0,1);
          if(j<m)add(id(i,j,2),id(i,j+1,4),0,1);
          if(i<n)add(id(i,j,3),id(i+1,j,1),0,1);
          if(j>1)add(id(i,j,4),id(i,j-1,2),0,1);
        }
      if(goal&1){puts("-1"); return 0;} goal>>=1;
      int flow=MCMF();
      if(flow==goal)printf("%d
    ",ans);
      else puts("-1");
      return 0;
    }

     

  • 相关阅读:
    MVC 传值
    LINQ to SQL 语句(2)之 Select/Distinct
    LINQ to SQL 语句(1)之 Where
    输入变量相同情况下,结果不正确
    vmware ubuntu 切换终端命令行
    汉字编码问题
    Windows API所提供的功能可以归为七类
    比较好的c++博文
    环境和库的引入
    C#调用Fortran生成的DLL的方法报内存不足
  • 原文地址:https://www.cnblogs.com/Zinn/p/10158533.html
Copyright © 2020-2023  润新知