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\n",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\n",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\n",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\n",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\n",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\n",ans);
  else puts("-1");
  return 0;
}

 

 

 

posted @ 2018-12-21 20:45  Zinn  阅读(206)  评论(0编辑  收藏  举报