1. 程式人生 > >bzoj 5120 無限之環 —— 費用流(多路增廣SPFA)

bzoj 5120 無限之環 —— 費用流(多路增廣SPFA)

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

神奇的費用流建圖;

首先,網格圖,相鄰之間有關係,所以先二分染色一下;

然後發現問題就是染色後黑白點之間要完美匹配插頭;

所以可以考慮把旋轉通過帶一些代價變成插頭方向的變化;

把一個格子拆成上下左右四個點,分類討論,連邊即可;

然而一開始連邊竟然寫了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;
}