题目: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跑出一些可行路线,然后 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; }